hevy-mcp 1.12.24 → 1.13.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
@@ -6,11 +6,11 @@
6
6
  import dotenvx from "@dotenvx/dotenvx";
7
7
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
- import { z as z42 } from "zod";
9
+ import { z as z6 } from "zod";
10
10
 
11
11
  // package.json
12
12
  var name = "hevy-mcp";
13
- var version = "1.12.23";
13
+ var version = "1.12.24";
14
14
 
15
15
  // src/tools/folders.ts
16
16
  import { z } from "zod";
@@ -54,13 +54,13 @@ function determineErrorType(error, message) {
54
54
  return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
55
55
  }
56
56
  function withErrorHandling(fn, context) {
57
- return (async (...args) => {
57
+ return async (args) => {
58
58
  try {
59
- return await fn(...args);
59
+ return await fn(args);
60
60
  } catch (error) {
61
61
  return createErrorResponse(error, context);
62
62
  }
63
- });
63
+ };
64
64
  }
65
65
 
66
66
  // src/utils/formatters.ts
@@ -190,49 +190,50 @@ function createEmptyResponse(message = "No data found") {
190
190
 
191
191
  // src/tools/folders.ts
192
192
  function registerFolderTools(server, hevyClient) {
193
+ const getRoutineFoldersSchema = {
194
+ page: z.coerce.number().int().gte(1).default(1),
195
+ pageSize: z.coerce.number().int().gte(1).lte(10).default(5)
196
+ };
193
197
  server.tool(
194
198
  "get-routine-folders",
195
199
  "Get a paginated list of your routine folders, including both default and custom folders. Useful for organizing and browsing your workout routines.",
196
- {
197
- page: z.coerce.number().int().gte(1).default(1),
198
- pageSize: z.coerce.number().int().gte(1).lte(10).default(5)
199
- },
200
- withErrorHandling(
201
- async ({ page, pageSize }) => {
202
- if (!hevyClient) {
203
- throw new Error(
204
- "API client not initialized. Please provide HEVY_API_KEY."
205
- );
206
- }
207
- const data = await hevyClient.getRoutineFolders({
208
- page,
209
- pageSize
210
- });
211
- const folders = data?.routine_folders?.map(
212
- (folder) => formatRoutineFolder(folder)
213
- ) || [];
214
- if (folders.length === 0) {
215
- return createEmptyResponse(
216
- "No routine folders found for the specified parameters"
217
- );
218
- }
219
- return createJsonResponse(folders);
220
- },
221
- "get-routine-folders"
222
- )
200
+ getRoutineFoldersSchema,
201
+ withErrorHandling(async (args) => {
202
+ if (!hevyClient) {
203
+ throw new Error(
204
+ "API client not initialized. Please provide HEVY_API_KEY."
205
+ );
206
+ }
207
+ const { page, pageSize } = args;
208
+ const data = await hevyClient.getRoutineFolders({
209
+ page,
210
+ pageSize
211
+ });
212
+ const folders = data?.routine_folders?.map(
213
+ (folder) => formatRoutineFolder(folder)
214
+ ) || [];
215
+ if (folders.length === 0) {
216
+ return createEmptyResponse(
217
+ "No routine folders found for the specified parameters"
218
+ );
219
+ }
220
+ return createJsonResponse(folders);
221
+ }, "get-routine-folders")
223
222
  );
223
+ const getRoutineFolderSchema = {
224
+ folderId: z.string().min(1)
225
+ };
224
226
  server.tool(
225
227
  "get-routine-folder",
226
228
  "Get complete details of a specific routine folder by its ID, including name, creation date, and associated routines.",
227
- {
228
- folderId: z.string().min(1)
229
- },
230
- withErrorHandling(async ({ folderId }) => {
229
+ getRoutineFolderSchema,
230
+ withErrorHandling(async (args) => {
231
231
  if (!hevyClient) {
232
232
  throw new Error(
233
233
  "API client not initialized. Please provide HEVY_API_KEY."
234
234
  );
235
235
  }
236
+ const { folderId } = args;
236
237
  const data = await hevyClient.getRoutineFolder(folderId);
237
238
  if (!data) {
238
239
  return createEmptyResponse(
@@ -243,18 +244,20 @@ function registerFolderTools(server, hevyClient) {
243
244
  return createJsonResponse(folder);
244
245
  }, "get-routine-folder")
245
246
  );
247
+ const createRoutineFolderSchema = {
248
+ name: z.string().min(1)
249
+ };
246
250
  server.tool(
247
251
  "create-routine-folder",
248
252
  "Create a new routine folder in your Hevy account. Requires a name for the folder. Returns the full folder details including the new folder ID.",
249
- {
250
- name: z.string().min(1)
251
- },
252
- withErrorHandling(async ({ name: name2 }) => {
253
+ createRoutineFolderSchema,
254
+ withErrorHandling(async (args) => {
253
255
  if (!hevyClient) {
254
256
  throw new Error(
255
257
  "API client not initialized. Please provide HEVY_API_KEY."
256
258
  );
257
259
  }
260
+ const { name: name2 } = args;
258
261
  const data = await hevyClient.createRoutineFolder({
259
262
  routine_folder: {
260
263
  title: name2
@@ -277,13 +280,14 @@ function registerFolderTools(server, hevyClient) {
277
280
  // src/tools/routines.ts
278
281
  import { z as z2 } from "zod";
279
282
  function registerRoutineTools(server, hevyClient) {
283
+ const getRoutinesSchema = {
284
+ page: z2.coerce.number().int().gte(1).default(1),
285
+ pageSize: z2.coerce.number().int().gte(1).lte(10).default(5)
286
+ };
280
287
  server.tool(
281
288
  "get-routines",
282
289
  "Get a paginated list of your workout routines, including custom and default routines. Useful for browsing or searching your available routines.",
283
- {
284
- page: z2.coerce.number().int().gte(1).default(1),
285
- pageSize: z2.coerce.number().int().gte(1).lte(10).default(5)
286
- },
290
+ getRoutinesSchema,
287
291
  withErrorHandling(async (args) => {
288
292
  if (!hevyClient) {
289
293
  throw new Error(
@@ -304,18 +308,20 @@ function registerRoutineTools(server, hevyClient) {
304
308
  return createJsonResponse(routines);
305
309
  }, "get-routines")
306
310
  );
311
+ const getRoutineSchema = {
312
+ routineId: z2.string().min(1)
313
+ };
307
314
  server.tool(
308
315
  "get-routine",
309
316
  "Get a routine by its ID using the direct endpoint. Returns all details for the specified routine.",
310
- {
311
- routineId: z2.string().min(1)
312
- },
313
- withErrorHandling(async ({ routineId }) => {
317
+ getRoutineSchema,
318
+ withErrorHandling(async (args) => {
314
319
  if (!hevyClient) {
315
320
  throw new Error(
316
321
  "API client not initialized. Please provide HEVY_API_KEY."
317
322
  );
318
323
  }
324
+ const { routineId } = args;
319
325
  const data = await hevyClient.getRoutineById(String(routineId));
320
326
  if (!data || !data.routine) {
321
327
  return createEmptyResponse(`Routine with ID ${routineId} not found`);
@@ -324,32 +330,33 @@ function registerRoutineTools(server, hevyClient) {
324
330
  return createJsonResponse(routine);
325
331
  }, "get-routine")
326
332
  );
333
+ const createRoutineSchema = {
334
+ title: z2.string().min(1),
335
+ folderId: z2.coerce.number().nullable().optional(),
336
+ notes: z2.string().optional(),
337
+ exercises: z2.array(
338
+ z2.object({
339
+ exerciseTemplateId: z2.string().min(1),
340
+ supersetId: z2.coerce.number().nullable().optional(),
341
+ restSeconds: z2.coerce.number().int().min(0).optional(),
342
+ notes: z2.string().optional(),
343
+ sets: z2.array(
344
+ z2.object({
345
+ type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
346
+ weightKg: z2.coerce.number().optional(),
347
+ reps: z2.coerce.number().int().optional(),
348
+ distanceMeters: z2.coerce.number().int().optional(),
349
+ durationSeconds: z2.coerce.number().int().optional(),
350
+ customMetric: z2.coerce.number().optional()
351
+ })
352
+ )
353
+ })
354
+ )
355
+ };
327
356
  server.tool(
328
357
  "create-routine",
329
358
  "Create a new workout routine in your Hevy account. Requires a title and at least one exercise with sets. Optionally assign to a folder. Returns the full routine details including the new routine ID.",
330
- {
331
- title: z2.string().min(1),
332
- folderId: z2.coerce.number().nullable().optional(),
333
- notes: z2.string().optional(),
334
- exercises: z2.array(
335
- z2.object({
336
- exerciseTemplateId: z2.string().min(1),
337
- supersetId: z2.coerce.number().nullable().optional(),
338
- restSeconds: z2.coerce.number().int().min(0).optional(),
339
- notes: z2.string().optional(),
340
- sets: z2.array(
341
- z2.object({
342
- type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
343
- weightKg: z2.coerce.number().optional(),
344
- reps: z2.coerce.number().int().optional(),
345
- distanceMeters: z2.coerce.number().int().optional(),
346
- durationSeconds: z2.coerce.number().int().optional(),
347
- customMetric: z2.coerce.number().optional()
348
- })
349
- )
350
- })
351
- )
352
- },
359
+ createRoutineSchema,
353
360
  withErrorHandling(async (args) => {
354
361
  if (!hevyClient) {
355
362
  throw new Error(
@@ -394,32 +401,33 @@ function registerRoutineTools(server, hevyClient) {
394
401
  });
395
402
  }, "create-routine")
396
403
  );
404
+ const updateRoutineSchema = {
405
+ routineId: z2.string().min(1),
406
+ title: z2.string().min(1),
407
+ notes: z2.string().optional(),
408
+ exercises: z2.array(
409
+ z2.object({
410
+ exerciseTemplateId: z2.string().min(1),
411
+ supersetId: z2.coerce.number().nullable().optional(),
412
+ restSeconds: z2.coerce.number().int().min(0).optional(),
413
+ notes: z2.string().optional(),
414
+ sets: z2.array(
415
+ z2.object({
416
+ type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
417
+ weightKg: z2.coerce.number().optional(),
418
+ reps: z2.coerce.number().int().optional(),
419
+ distanceMeters: z2.coerce.number().int().optional(),
420
+ durationSeconds: z2.coerce.number().int().optional(),
421
+ customMetric: z2.coerce.number().optional()
422
+ })
423
+ )
424
+ })
425
+ )
426
+ };
397
427
  server.tool(
398
428
  "update-routine",
399
429
  "Update an existing routine by ID. You can modify the title, notes, and exercise configurations. Returns the updated routine with all changes applied.",
400
- {
401
- routineId: z2.string().min(1),
402
- title: z2.string().min(1),
403
- notes: z2.string().optional(),
404
- exercises: z2.array(
405
- z2.object({
406
- exerciseTemplateId: z2.string().min(1),
407
- supersetId: z2.coerce.number().nullable().optional(),
408
- restSeconds: z2.coerce.number().int().min(0).optional(),
409
- notes: z2.string().optional(),
410
- sets: z2.array(
411
- z2.object({
412
- type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
413
- weightKg: z2.coerce.number().optional(),
414
- reps: z2.coerce.number().int().optional(),
415
- distanceMeters: z2.coerce.number().int().optional(),
416
- durationSeconds: z2.coerce.number().int().optional(),
417
- customMetric: z2.coerce.number().optional()
418
- })
419
- )
420
- })
421
- )
422
- },
430
+ updateRoutineSchema,
423
431
  withErrorHandling(async (args) => {
424
432
  if (!hevyClient) {
425
433
  throw new Error(
@@ -468,509 +476,65 @@ function registerRoutineTools(server, hevyClient) {
468
476
  // src/tools/templates.ts
469
477
  import { z as z3 } from "zod";
470
478
  function registerTemplateTools(server, hevyClient) {
479
+ const getExerciseTemplatesSchema = {
480
+ page: z3.coerce.number().int().gte(1).default(1),
481
+ pageSize: z3.coerce.number().int().gte(1).lte(100).default(5)
482
+ };
471
483
  server.tool(
472
484
  "get-exercise-templates",
473
485
  "Get a paginated list of exercise templates (default and custom) with details like name, category, equipment, and muscle groups. Useful for browsing or searching available exercises.",
474
- {
475
- page: z3.coerce.number().int().gte(1).default(1),
476
- pageSize: z3.coerce.number().int().gte(1).lte(100).default(5)
477
- },
478
- withErrorHandling(
479
- async ({ page, pageSize }) => {
480
- if (!hevyClient) {
481
- throw new Error(
482
- "API client not initialized. Please provide HEVY_API_KEY."
483
- );
484
- }
485
- const data = await hevyClient.getExerciseTemplates({
486
- page,
487
- pageSize
488
- });
489
- const templates = data?.exercise_templates?.map(
490
- (template) => formatExerciseTemplate(template)
491
- ) || [];
492
- if (templates.length === 0) {
493
- return createEmptyResponse(
494
- "No exercise templates found for the specified parameters"
495
- );
496
- }
497
- return createJsonResponse(templates);
498
- },
499
- "get-exercise-templates"
500
- )
486
+ getExerciseTemplatesSchema,
487
+ withErrorHandling(async (args) => {
488
+ if (!hevyClient) {
489
+ throw new Error(
490
+ "API client not initialized. Please provide HEVY_API_KEY."
491
+ );
492
+ }
493
+ const { page, pageSize } = args;
494
+ const data = await hevyClient.getExerciseTemplates({
495
+ page,
496
+ pageSize
497
+ });
498
+ const templates = data?.exercise_templates?.map(
499
+ (template) => formatExerciseTemplate(template)
500
+ ) || [];
501
+ if (templates.length === 0) {
502
+ return createEmptyResponse(
503
+ "No exercise templates found for the specified parameters"
504
+ );
505
+ }
506
+ return createJsonResponse(templates);
507
+ }, "get-exercise-templates")
501
508
  );
509
+ const getExerciseTemplateSchema = {
510
+ exerciseTemplateId: z3.string().min(1)
511
+ };
502
512
  server.tool(
503
513
  "get-exercise-template",
504
514
  "Get complete details of a specific exercise template by its ID, including name, category, equipment, muscle groups, and notes.",
505
- {
506
- exerciseTemplateId: z3.string().min(1)
507
- },
508
- withErrorHandling(
509
- async ({ exerciseTemplateId }) => {
510
- if (!hevyClient) {
511
- throw new Error(
512
- "API client not initialized. Please provide HEVY_API_KEY."
513
- );
514
- }
515
- const data = await hevyClient.getExerciseTemplate(exerciseTemplateId);
516
- if (!data) {
517
- return createEmptyResponse(
518
- `Exercise template with ID ${exerciseTemplateId} not found`
519
- );
520
- }
521
- const template = formatExerciseTemplate(data);
522
- return createJsonResponse(template);
523
- },
524
- "get-exercise-template"
525
- )
515
+ getExerciseTemplateSchema,
516
+ withErrorHandling(async (args) => {
517
+ if (!hevyClient) {
518
+ throw new Error(
519
+ "API client not initialized. Please provide HEVY_API_KEY."
520
+ );
521
+ }
522
+ const { exerciseTemplateId } = args;
523
+ const data = await hevyClient.getExerciseTemplate(exerciseTemplateId);
524
+ if (!data) {
525
+ return createEmptyResponse(
526
+ `Exercise template with ID ${exerciseTemplateId} not found`
527
+ );
528
+ }
529
+ const template = formatExerciseTemplate(data);
530
+ return createJsonResponse(template);
531
+ }, "get-exercise-template")
526
532
  );
527
533
  }
528
534
 
529
535
  // src/tools/webhooks.ts
530
- import { z as z40 } from "zod";
531
-
532
- // src/generated/client/schemas/deletedWorkoutSchema.ts
533
536
  import { z as z4 } from "zod";
534
- var deletedWorkoutSchema = z4.object({
535
- "type": z4.string().describe("Indicates the type of the event (deleted)"),
536
- "id": z4.string().describe("The unique identifier of the deleted workout"),
537
- "deleted_at": z4.optional(z4.string().describe("A date string indicating when the workout was deleted"))
538
- });
539
-
540
- // src/generated/client/schemas/deleteV1WebhookSubscriptionSchema.ts
541
- import { z as z5 } from "zod";
542
- var deleteV1WebhookSubscriptionHeaderParamsSchema = z5.object({
543
- "api-key": z5.string().uuid().describe("Your API key")
544
- });
545
- var deleteV1WebhookSubscription200Schema = z5.any();
546
-
547
- // src/generated/client/schemas/exerciseTemplateSchema.ts
548
- import { z as z6 } from "zod";
549
- var exerciseTemplateSchema = z6.object({
550
- "id": z6.optional(z6.string().describe("The exercise template ID.")),
551
- "title": z6.optional(z6.string().describe("The exercise title.")),
552
- "type": z6.optional(z6.string().describe("The exercise type.")),
553
- "primary_muscle_group": z6.optional(z6.string().describe("The primary muscle group of the exercise.")),
554
- "secondary_muscle_groups": z6.optional(z6.array(z6.string()).describe("The secondary muscle groups of the exercise.")),
555
- "is_custom": z6.optional(z6.boolean().describe("A boolean indicating whether the exercise is a custom exercise."))
556
- });
557
-
558
- // src/generated/client/schemas/getV1ExerciseTemplatesExercisetemplateidSchema.ts
559
- import { z as z7 } from "zod";
560
- var getV1ExerciseTemplatesExercisetemplateidPathParamsSchema = z7.object({
561
- "exerciseTemplateId": z7.any()
562
- });
563
- var getV1ExerciseTemplatesExercisetemplateidHeaderParamsSchema = z7.object({
564
- "api-key": z7.string().uuid()
565
- });
566
- var getV1ExerciseTemplatesExercisetemplateid404Schema = z7.any();
567
-
568
- // src/generated/client/schemas/getV1ExerciseTemplatesSchema.ts
569
- import { z as z8 } from "zod";
570
- var getV1ExerciseTemplatesQueryParamsSchema = z8.object({
571
- "page": z8.coerce.number().int().default(1).describe("Page number (Must be 1 or greater)"),
572
- "pageSize": z8.coerce.number().int().default(5).describe("Number of items on the requested page (Max 100)")
573
- });
574
- var getV1ExerciseTemplatesHeaderParamsSchema = z8.object({
575
- "api-key": z8.string().uuid()
576
- });
577
- var getV1ExerciseTemplates200Schema = z8.object({
578
- "page": z8.optional(z8.number().int().default(1).describe("Current page number")),
579
- "page_count": z8.optional(z8.number().int().default(5).describe("Total number of pages")),
580
- "exercise_templates": z8.optional(z8.array(z8.lazy(() => exerciseTemplateSchema)))
581
- });
582
- var getV1ExerciseTemplates400Schema = z8.any();
583
-
584
- // src/generated/client/schemas/routineFolderSchema.ts
585
- import { z as z9 } from "zod";
586
- var routineFolderSchema = z9.object({
587
- "id": z9.optional(z9.number().describe("The routine folder ID.")),
588
- "index": z9.optional(z9.number().describe("The routine folder index. Describes the order of the folder in the list.")),
589
- "title": z9.optional(z9.string().describe("The routine folder title.")),
590
- "updated_at": z9.optional(z9.string().describe("ISO 8601 timestamp of when the folder was last updated.")),
591
- "created_at": z9.optional(z9.string().describe("ISO 8601 timestamp of when the folder was created."))
592
- });
593
-
594
- // src/generated/client/schemas/getV1RoutineFoldersFolderidSchema.ts
595
- import { z as z10 } from "zod";
596
- var getV1RoutineFoldersFolderidPathParamsSchema = z10.object({
597
- "folderId": z10.any()
598
- });
599
- var getV1RoutineFoldersFolderidHeaderParamsSchema = z10.object({
600
- "api-key": z10.string().uuid()
601
- });
602
- var getV1RoutineFoldersFolderid404Schema = z10.any();
603
-
604
- // src/generated/client/schemas/getV1RoutineFoldersSchema.ts
605
- import { z as z11 } from "zod";
606
- var getV1RoutineFoldersQueryParamsSchema = z11.object({
607
- "page": z11.coerce.number().int().default(1).describe("Page number (Must be 1 or greater)"),
608
- "pageSize": z11.coerce.number().int().default(5).describe("Number of items on the requested page (Max 10)")
609
- });
610
- var getV1RoutineFoldersHeaderParamsSchema = z11.object({
611
- "api-key": z11.string().uuid()
612
- });
613
- var getV1RoutineFolders200Schema = z11.object({
614
- "page": z11.optional(z11.number().int().default(1).describe("Current page number")),
615
- "page_count": z11.optional(z11.number().int().default(5).describe("Total number of pages")),
616
- "routine_folders": z11.optional(z11.array(z11.lazy(() => routineFolderSchema)))
617
- });
618
- var getV1RoutineFolders400Schema = z11.any();
619
-
620
- // src/generated/client/schemas/routineSchema.ts
621
- import { z as z12 } from "zod";
622
- var routineSchema = z12.object({
623
- "id": z12.optional(z12.string().describe("The routine ID.")),
624
- "title": z12.optional(z12.string().describe("The routine title.")),
625
- "folder_id": z12.number().describe("The routine folder ID.").nullish(),
626
- "updated_at": z12.optional(z12.string().describe("ISO 8601 timestamp of when the routine was last updated.")),
627
- "created_at": z12.optional(z12.string().describe("ISO 8601 timestamp of when the routine was created.")),
628
- "exercises": z12.optional(z12.array(z12.object({
629
- "index": z12.optional(z12.number().describe("Index indicating the order of the exercise in the routine.")),
630
- "title": z12.optional(z12.string().describe("Title of the exercise")),
631
- "rest_seconds": z12.optional(z12.string().describe("The rest time in seconds between sets of the exercise")),
632
- "notes": z12.optional(z12.string().describe("Routine notes on the exercise")),
633
- "exercise_template_id": z12.optional(z12.string().describe("The id of the exercise template. This can be used to fetch the exercise template.")),
634
- "supersets_id": z12.number().describe("The id of the superset that the exercise belongs to. A value of null indicates the exercise is not part of a superset.").nullish(),
635
- "sets": z12.optional(z12.array(z12.object({
636
- "index": z12.optional(z12.number().describe("Index indicating the order of the set in the routine.")),
637
- "type": z12.optional(z12.string().describe("The type of set. This can be one of 'normal', 'warmup', 'dropset', 'failure'")),
638
- "weight_kg": z12.number().describe("Weight lifted in kilograms.").nullish(),
639
- "reps": z12.number().describe("Number of reps logged for the set").nullish(),
640
- "rep_range": z12.object({
641
- "start": z12.number().describe("Starting rep count for the range").nullish(),
642
- "end": z12.number().describe("Ending rep count for the range").nullish()
643
- }).describe("Range of reps for the set, if applicable").nullish(),
644
- "distance_meters": z12.number().describe("Number of meters logged for the set").nullish(),
645
- "duration_seconds": z12.number().describe("Number of seconds logged for the set").nullish(),
646
- "rpe": z12.number().describe("RPE (Relative perceived exertion) value logged for the set").nullish(),
647
- "custom_metric": z12.number().describe("Custom metric logged for the set (Currently only used to log floors or steps for stair machine exercises)").nullish()
648
- })))
649
- })))
650
- });
651
-
652
- // src/generated/client/schemas/getV1RoutinesRoutineidSchema.ts
653
- import { z as z13 } from "zod";
654
- var getV1RoutinesRoutineidPathParamsSchema = z13.object({
655
- "routineId": z13.any()
656
- });
657
- var getV1RoutinesRoutineidHeaderParamsSchema = z13.object({
658
- "api-key": z13.string().uuid()
659
- });
660
- var getV1RoutinesRoutineid200Schema = z13.object({
661
- "routine": z13.optional(z13.lazy(() => routineSchema))
662
- });
663
- var getV1RoutinesRoutineid400Schema = z13.object({
664
- "error": z13.optional(z13.string().describe("Error message"))
665
- });
666
-
667
- // src/generated/client/schemas/getV1RoutinesSchema.ts
668
- import { z as z14 } from "zod";
669
- var getV1RoutinesQueryParamsSchema = z14.object({
670
- "page": z14.coerce.number().int().default(1).describe("Page number (Must be 1 or greater)"),
671
- "pageSize": z14.coerce.number().int().default(5).describe("Number of items on the requested page (Max 10)")
672
- });
673
- var getV1RoutinesHeaderParamsSchema = z14.object({
674
- "api-key": z14.string().uuid()
675
- });
676
- var getV1Routines200Schema = z14.object({
677
- "page": z14.optional(z14.number().int().describe("Current page number")),
678
- "page_count": z14.optional(z14.number().int().describe("Total number of pages")),
679
- "routines": z14.optional(z14.array(z14.lazy(() => routineSchema)))
680
- });
681
- var getV1Routines400Schema = z14.any();
682
-
683
- // src/generated/client/schemas/getV1WebhookSubscriptionSchema.ts
684
- import { z as z15 } from "zod";
685
- var getV1WebhookSubscriptionHeaderParamsSchema = z15.object({
686
- "api-key": z15.string().uuid().describe("Your API key")
687
- });
688
- var getV1WebhookSubscription200Schema = z15.object({
689
- "url": z15.optional(z15.string().describe("The webhook URL")),
690
- "auth_token": z15.optional(z15.string().describe("The auth token for the webhook"))
691
- });
692
- var getV1WebhookSubscription404Schema = z15.any();
693
-
694
- // src/generated/client/schemas/getV1WorkoutsCountSchema.ts
695
- import { z as z16 } from "zod";
696
- var getV1WorkoutsCountHeaderParamsSchema = z16.object({
697
- "api-key": z16.string().uuid()
698
- });
699
- var getV1WorkoutsCount200Schema = z16.object({
700
- "workout_count": z16.optional(z16.number().int().default(42).describe("The total number of workouts"))
701
- });
702
-
703
- // src/generated/client/schemas/workoutSchema.ts
704
- import { z as z17 } from "zod";
705
- var workoutSchema = z17.object({
706
- "id": z17.optional(z17.string().describe("The workout ID.")),
707
- "title": z17.optional(z17.string().describe("The workout title.")),
708
- "description": z17.optional(z17.string().describe("The workout description.")),
709
- "start_time": z17.optional(z17.number().describe("ISO 8601 timestamp of when the workout was recorded to have started.")),
710
- "end_time": z17.optional(z17.number().describe("ISO 8601 timestamp of when the workout was recorded to have ended.")),
711
- "updated_at": z17.optional(z17.string().describe("ISO 8601 timestamp of when the workout was last updated.")),
712
- "created_at": z17.optional(z17.string().describe("ISO 8601 timestamp of when the workout was created.")),
713
- "exercises": z17.optional(z17.array(z17.object({
714
- "index": z17.optional(z17.number().describe("Index indicating the order of the exercise in the workout.")),
715
- "title": z17.optional(z17.string().describe("Title of the exercise")),
716
- "notes": z17.optional(z17.string().describe("Notes on the exercise")),
717
- "exercise_template_id": z17.optional(z17.string().describe("The id of the exercise template. This can be used to fetch the exercise template.")),
718
- "supersets_id": z17.number().describe("The id of the superset that the exercise belongs to. A value of null indicates the exercise is not part of a superset.").nullish(),
719
- "sets": z17.optional(z17.array(z17.object({
720
- "index": z17.optional(z17.number().describe("Index indicating the order of the set in the workout.")),
721
- "type": z17.optional(z17.string().describe("The type of set. This can be one of 'normal', 'warmup', 'dropset', 'failure'")),
722
- "weight_kg": z17.number().describe("Weight lifted in kilograms.").nullish(),
723
- "reps": z17.number().describe("Number of reps logged for the set").nullish(),
724
- "distance_meters": z17.number().describe("Number of meters logged for the set").nullish(),
725
- "duration_seconds": z17.number().describe("Number of seconds logged for the set").nullish(),
726
- "rpe": z17.number().describe("RPE (Relative perceived exertion) value logged for the set").nullish(),
727
- "custom_metric": z17.number().describe("Custom metric logged for the set (Currently only used to log floors or steps for stair machine exercises)").nullish()
728
- })))
729
- })))
730
- });
731
-
732
- // src/generated/client/schemas/updatedWorkoutSchema.ts
733
- import { z as z18 } from "zod";
734
- var updatedWorkoutSchema = z18.object({
735
- "type": z18.string().describe("Indicates the type of the event (updated)"),
736
- "workout": z18.lazy(() => workoutSchema)
737
- });
738
-
739
- // src/generated/client/schemas/paginatedWorkoutEventsSchema.ts
740
- import { z as z19 } from "zod";
741
- var paginatedWorkoutEventsSchema = z19.object({
742
- "page": z19.number().int().describe("The current page number"),
743
- "page_count": z19.number().int().describe("The total number of pages available"),
744
- "events": z19.array(z19.union([z19.lazy(() => updatedWorkoutSchema), z19.lazy(() => deletedWorkoutSchema)])).describe("An array of workout events (either updated or deleted)")
745
- });
746
-
747
- // src/generated/client/schemas/getV1WorkoutsEventsSchema.ts
748
- import { z as z20 } from "zod";
749
- var getV1WorkoutsEventsQueryParamsSchema = z20.object({
750
- "page": z20.coerce.number().int().default(1).describe("Page number (Must be 1 or greater)"),
751
- "pageSize": z20.coerce.number().int().default(5).describe("Number of items on the requested page (Max 10)"),
752
- "since": z20.string().default("1970-01-01T00:00:00Z")
753
- });
754
- var getV1WorkoutsEventsHeaderParamsSchema = z20.object({
755
- "api-key": z20.string().uuid()
756
- });
757
- var getV1WorkoutsEvents500Schema = z20.any();
758
-
759
- // src/generated/client/schemas/getV1WorkoutsSchema.ts
760
- import { z as z21 } from "zod";
761
- var getV1WorkoutsQueryParamsSchema = z21.object({
762
- "page": z21.coerce.number().int().default(1).describe("Page number (Must be 1 or greater)"),
763
- "pageSize": z21.coerce.number().int().default(5).describe("Number of items on the requested page (Max 10)")
764
- });
765
- var getV1WorkoutsHeaderParamsSchema = z21.object({
766
- "api-key": z21.string().uuid()
767
- });
768
- var getV1Workouts200Schema = z21.object({
769
- "page": z21.optional(z21.number().int().describe("Current page number")),
770
- "page_count": z21.optional(z21.number().int().describe("Total number of pages")),
771
- "workouts": z21.optional(z21.array(z21.lazy(() => workoutSchema)))
772
- });
773
- var getV1Workouts400Schema = z21.any();
774
-
775
- // src/generated/client/schemas/getV1WorkoutsWorkoutidSchema.ts
776
- import { z as z22 } from "zod";
777
- var getV1WorkoutsWorkoutidPathParamsSchema = z22.object({
778
- "workoutId": z22.any()
779
- });
780
- var getV1WorkoutsWorkoutidHeaderParamsSchema = z22.object({
781
- "api-key": z22.string().uuid()
782
- });
783
- var getV1WorkoutsWorkoutid404Schema = z22.any();
784
-
785
- // src/generated/client/schemas/postRoutineFolderRequestBodySchema.ts
786
- import { z as z23 } from "zod";
787
- var postRoutineFolderRequestBodySchema = z23.object({
788
- "routine_folder": z23.optional(z23.object({
789
- "title": z23.optional(z23.string().describe("The title of the routine folder."))
790
- }))
791
- });
792
-
793
- // src/generated/client/schemas/postRoutinesRequestSetSchema.ts
794
- import { z as z24 } from "zod";
795
- var postRoutinesRequestSetSchema = z24.object({
796
- "type": z24.optional(z24.enum(["warmup", "normal", "failure", "dropset"]).describe("The type of the set.")),
797
- "weight_kg": z24.number().describe("The weight in kilograms.").nullish(),
798
- "reps": z24.number().int().describe("The number of repetitions.").nullish(),
799
- "distance_meters": z24.number().int().describe("The distance in meters.").nullish(),
800
- "duration_seconds": z24.number().int().describe("The duration in seconds.").nullish(),
801
- "custom_metric": z24.number().describe("A custom metric for the set. Currently used for steps and floors.").nullish(),
802
- "rep_range": z24.object({
803
- "start": z24.number().describe("Starting rep count for the range").nullish(),
804
- "end": z24.number().describe("Ending rep count for the range").nullish()
805
- }).describe("Range of reps for the set, if applicable").nullish()
806
- });
807
-
808
- // src/generated/client/schemas/postRoutinesRequestExerciseSchema.ts
809
- import { z as z25 } from "zod";
810
- var postRoutinesRequestExerciseSchema = z25.object({
811
- "exercise_template_id": z25.optional(z25.string().describe("The ID of the exercise template.")),
812
- "superset_id": z25.number().int().describe("The ID of the superset.").nullish(),
813
- "rest_seconds": z25.number().int().describe("The rest time in seconds.").nullish(),
814
- "notes": z25.string().describe("Additional notes for the exercise.").nullish(),
815
- "sets": z25.optional(z25.array(z25.lazy(() => postRoutinesRequestSetSchema)))
816
- });
817
-
818
- // src/generated/client/schemas/postRoutinesRequestBodySchema.ts
819
- import { z as z26 } from "zod";
820
- var postRoutinesRequestBodySchema = z26.object({
821
- "routine": z26.optional(z26.object({
822
- "title": z26.optional(z26.string().describe("The title of the routine.")),
823
- "folder_id": z26.number().describe('The folder id the routine should be added to. Pass null to insert the routine into default "My Routines" folder').nullish(),
824
- "notes": z26.optional(z26.string().describe("Additional notes for the routine.")),
825
- "exercises": z26.optional(z26.array(z26.lazy(() => postRoutinesRequestExerciseSchema)))
826
- }))
827
- });
828
-
829
- // src/generated/client/schemas/postV1RoutineFoldersSchema.ts
830
- import { z as z27 } from "zod";
831
- var postV1RoutineFoldersHeaderParamsSchema = z27.object({
832
- "api-key": z27.string().uuid()
833
- });
834
- var postV1RoutineFolders400Schema = z27.object({
835
- "error": z27.optional(z27.string().describe("Error message"))
836
- });
837
-
838
- // src/generated/client/schemas/postV1RoutinesSchema.ts
839
- import { z as z28 } from "zod";
840
- var postV1RoutinesHeaderParamsSchema = z28.object({
841
- "api-key": z28.string().uuid()
842
- });
843
- var postV1Routines400Schema = z28.object({
844
- "error": z28.optional(z28.string().describe("Error message"))
845
- });
846
- var postV1Routines403Schema = z28.object({
847
- "error": z28.optional(z28.string().describe("Error message"))
848
- });
849
-
850
- // src/generated/client/schemas/webhookRequestBodySchema.ts
851
- import { z as z29 } from "zod";
852
- var webhookRequestBodySchema = z29.object({
853
- "authToken": z29.optional(z29.string().describe("The auth token that will be send as Authorization header in the webhook.")),
854
- "url": z29.optional(z29.string().describe("The webhook URL."))
855
- });
856
-
857
- // src/generated/client/schemas/postV1WebhookSubscriptionSchema.ts
858
- import { z as z30 } from "zod";
859
- var postV1WebhookSubscriptionHeaderParamsSchema = z30.object({
860
- "api-key": z30.string().uuid()
861
- });
862
- var postV1WebhookSubscription201Schema = z30.any();
863
- var postV1WebhookSubscription400Schema = z30.object({
864
- "error": z30.optional(z30.string().describe("Error message"))
865
- });
866
-
867
- // src/generated/client/schemas/postWorkoutsRequestSetSchema.ts
868
- import { z as z31 } from "zod";
869
- var postWorkoutsRequestSetSchema = z31.object({
870
- "type": z31.optional(z31.enum(["warmup", "normal", "failure", "dropset"]).describe("The type of the set.")),
871
- "weight_kg": z31.number().describe("The weight in kilograms.").nullish(),
872
- "reps": z31.number().int().describe("The number of repetitions.").nullish(),
873
- "distance_meters": z31.number().int().describe("The distance in meters.").nullish(),
874
- "duration_seconds": z31.number().int().describe("The duration in seconds.").nullish(),
875
- "custom_metric": z31.number().describe("A custom metric for the set. Currently used for steps and floors.").nullish(),
876
- "rpe": z31.union([z31.literal(6), z31.literal(7), z31.literal(7.5), z31.literal(8), z31.literal(8.5), z31.literal(9), z31.literal(9.5), z31.literal(10)]).describe("The Rating of Perceived Exertion (RPE).").nullish()
877
- });
878
-
879
- // src/generated/client/schemas/postWorkoutsRequestExerciseSchema.ts
880
- import { z as z32 } from "zod";
881
- var postWorkoutsRequestExerciseSchema = z32.object({
882
- "exercise_template_id": z32.optional(z32.string().describe("The ID of the exercise template.")),
883
- "superset_id": z32.number().int().describe("The ID of the superset.").nullish(),
884
- "notes": z32.string().describe("Additional notes for the exercise.").nullish(),
885
- "sets": z32.optional(z32.array(z32.lazy(() => postWorkoutsRequestSetSchema)))
886
- });
887
-
888
- // src/generated/client/schemas/postWorkoutsRequestBodySchema.ts
889
- import { z as z33 } from "zod";
890
- var postWorkoutsRequestBodySchema = z33.object({
891
- "workout": z33.optional(z33.object({
892
- "title": z33.optional(z33.string().describe("The title of the workout.")),
893
- "description": z33.string().describe("A description for the workout workout.").nullish(),
894
- "start_time": z33.optional(z33.string().describe("The time the workout started.")),
895
- "end_time": z33.optional(z33.string().describe("The time the workout ended.")),
896
- "is_private": z33.optional(z33.boolean().describe("A boolean indicating if the workout is private.")),
897
- "exercises": z33.optional(z33.array(z33.lazy(() => postWorkoutsRequestExerciseSchema)))
898
- }))
899
- });
900
-
901
- // src/generated/client/schemas/postV1WorkoutsSchema.ts
902
- import { z as z34 } from "zod";
903
- var postV1WorkoutsHeaderParamsSchema = z34.object({
904
- "api-key": z34.string().uuid()
905
- });
906
- var postV1Workouts400Schema = z34.object({
907
- "error": z34.optional(z34.string().describe("Error message"))
908
- });
909
-
910
- // src/generated/client/schemas/putRoutinesRequestSetSchema.ts
911
- import { z as z35 } from "zod";
912
- var putRoutinesRequestSetSchema = z35.object({
913
- "type": z35.optional(z35.enum(["warmup", "normal", "failure", "dropset"]).describe("The type of the set.")),
914
- "weight_kg": z35.number().describe("The weight in kilograms.").nullish(),
915
- "reps": z35.number().int().describe("The number of repetitions.").nullish(),
916
- "distance_meters": z35.number().int().describe("The distance in meters.").nullish(),
917
- "duration_seconds": z35.number().int().describe("The duration in seconds.").nullish(),
918
- "custom_metric": z35.number().describe("A custom metric for the set. Currently used for steps and floors.").nullish(),
919
- "rep_range": z35.object({
920
- "start": z35.number().describe("Starting rep count for the range").nullish(),
921
- "end": z35.number().describe("Ending rep count for the range").nullish()
922
- }).describe("Range of reps for the set, if applicable").nullish()
923
- });
924
-
925
- // src/generated/client/schemas/putRoutinesRequestExerciseSchema.ts
926
- import { z as z36 } from "zod";
927
- var putRoutinesRequestExerciseSchema = z36.object({
928
- "exercise_template_id": z36.optional(z36.string().describe("The ID of the exercise template.")),
929
- "superset_id": z36.number().int().describe("The ID of the superset.").nullish(),
930
- "rest_seconds": z36.number().int().describe("The rest time in seconds.").nullish(),
931
- "notes": z36.string().describe("Additional notes for the exercise.").nullish(),
932
- "sets": z36.optional(z36.array(z36.lazy(() => putRoutinesRequestSetSchema)))
933
- });
934
-
935
- // src/generated/client/schemas/putRoutinesRequestBodySchema.ts
936
- import { z as z37 } from "zod";
937
- var putRoutinesRequestBodySchema = z37.object({
938
- "routine": z37.optional(z37.object({
939
- "title": z37.optional(z37.string().describe("The title of the routine.")),
940
- "notes": z37.string().describe("Additional notes for the routine.").nullish(),
941
- "exercises": z37.optional(z37.array(z37.lazy(() => putRoutinesRequestExerciseSchema)))
942
- }))
943
- });
944
-
945
- // src/generated/client/schemas/putV1RoutinesRoutineidSchema.ts
946
- import { z as z38 } from "zod";
947
- var putV1RoutinesRoutineidPathParamsSchema = z38.object({
948
- "routineId": z38.any()
949
- });
950
- var putV1RoutinesRoutineidHeaderParamsSchema = z38.object({
951
- "api-key": z38.string().uuid()
952
- });
953
- var putV1RoutinesRoutineid400Schema = z38.object({
954
- "error": z38.optional(z38.string().describe("Error message"))
955
- });
956
- var putV1RoutinesRoutineid404Schema = z38.object({
957
- "error": z38.optional(z38.string().describe("Error message"))
958
- });
959
-
960
- // src/generated/client/schemas/putV1WorkoutsWorkoutidSchema.ts
961
- import { z as z39 } from "zod";
962
- var putV1WorkoutsWorkoutidPathParamsSchema = z39.object({
963
- "workoutId": z39.any()
964
- });
965
- var putV1WorkoutsWorkoutidHeaderParamsSchema = z39.object({
966
- "api-key": z39.string().uuid()
967
- });
968
- var putV1WorkoutsWorkoutid400Schema = z39.object({
969
- "error": z39.optional(z39.string().describe("Error message"))
970
- });
971
-
972
- // src/tools/webhooks.ts
973
- var webhookUrlSchema = z40.string().url().refine(
537
+ var webhookUrlSchema = z4.string().url().refine(
974
538
  (url) => {
975
539
  try {
976
540
  const parsed = new URL(url);
@@ -996,16 +560,22 @@ var webhookUrlSchema = z40.string().url().refine(
996
560
  }
997
561
  );
998
562
  function registerWebhookTools(server, hevyClient) {
563
+ const getWebhookSubscriptionSchema = {};
999
564
  server.tool(
1000
565
  "get-webhook-subscription",
1001
566
  "Get the current webhook subscription for this account. Returns the webhook URL and auth token if a subscription exists.",
1002
- {},
1003
- withErrorHandling(async () => {
567
+ getWebhookSubscriptionSchema,
568
+ withErrorHandling(async (_args) => {
1004
569
  if (!hevyClient) {
1005
570
  throw new Error(
1006
571
  "API client not initialized. Please provide HEVY_API_KEY."
1007
572
  );
1008
573
  }
574
+ if (!("getWebhookSubscription" in hevyClient)) {
575
+ throw new Error(
576
+ "Webhook subscription API not available. Please regenerate the client from the updated OpenAPI spec."
577
+ );
578
+ }
1009
579
  const data = await hevyClient.getWebhookSubscription();
1010
580
  if (!data) {
1011
581
  return createEmptyResponse(
@@ -1015,28 +585,36 @@ function registerWebhookTools(server, hevyClient) {
1015
585
  return createJsonResponse(data);
1016
586
  }, "get-webhook-subscription")
1017
587
  );
588
+ const createWebhookSubscriptionSchema = {
589
+ url: webhookUrlSchema.describe(
590
+ "The webhook URL that will receive POST requests when workouts are created"
591
+ ),
592
+ authToken: z4.string().optional().describe(
593
+ "Optional auth token that will be sent as Authorization header in webhook requests"
594
+ )
595
+ };
1018
596
  server.tool(
1019
597
  "create-webhook-subscription",
1020
598
  "Create a new webhook subscription for this account. The webhook will receive POST requests when workouts are created. Your endpoint must respond with 200 OK within 5 seconds.",
1021
- {
1022
- url: webhookUrlSchema.describe(
1023
- "The webhook URL that will receive POST requests when workouts are created"
1024
- ),
1025
- authToken: z40.string().optional().describe(
1026
- "Optional auth token that will be sent as Authorization header in webhook requests"
1027
- )
1028
- },
1029
- withErrorHandling(async ({ url, authToken }) => {
599
+ createWebhookSubscriptionSchema,
600
+ withErrorHandling(async (args) => {
1030
601
  if (!hevyClient) {
1031
602
  throw new Error(
1032
603
  "API client not initialized. Please provide HEVY_API_KEY."
1033
604
  );
1034
605
  }
1035
- const requestBody = webhookRequestBodySchema.parse({
1036
- url,
1037
- authToken
606
+ const { url, authToken } = args;
607
+ if (!("createWebhookSubscription" in hevyClient)) {
608
+ throw new Error(
609
+ "Webhook subscription API not available. Please regenerate the client from the updated OpenAPI spec."
610
+ );
611
+ }
612
+ const data = await hevyClient.createWebhookSubscription({
613
+ webhook: {
614
+ url,
615
+ authToken: authToken || null
616
+ }
1038
617
  });
1039
- const data = await hevyClient.createWebhookSubscription(requestBody);
1040
618
  if (!data) {
1041
619
  return createEmptyResponse(
1042
620
  "Failed to create webhook subscription - please check your URL and try again"
@@ -1045,16 +623,22 @@ function registerWebhookTools(server, hevyClient) {
1045
623
  return createJsonResponse(data);
1046
624
  }, "create-webhook-subscription")
1047
625
  );
626
+ const deleteWebhookSubscriptionSchema = {};
1048
627
  server.tool(
1049
628
  "delete-webhook-subscription",
1050
629
  "Delete the current webhook subscription for this account. This will stop all webhook notifications.",
1051
- {},
1052
- withErrorHandling(async () => {
630
+ deleteWebhookSubscriptionSchema,
631
+ withErrorHandling(async (_args) => {
1053
632
  if (!hevyClient) {
1054
633
  throw new Error(
1055
634
  "API client not initialized. Please provide HEVY_API_KEY."
1056
635
  );
1057
636
  }
637
+ if (!("deleteWebhookSubscription" in hevyClient)) {
638
+ throw new Error(
639
+ "Webhook subscription API not available. Please regenerate the client from the updated OpenAPI spec."
640
+ );
641
+ }
1058
642
  const data = await hevyClient.deleteWebhookSubscription();
1059
643
  if (!data) {
1060
644
  return createEmptyResponse(
@@ -1067,21 +651,23 @@ function registerWebhookTools(server, hevyClient) {
1067
651
  }
1068
652
 
1069
653
  // src/tools/workouts.ts
1070
- import { z as z41 } from "zod";
654
+ import { z as z5 } from "zod";
1071
655
  function registerWorkoutTools(server, hevyClient) {
656
+ const getWorkoutsSchema = {
657
+ page: z5.coerce.number().gte(1).default(1),
658
+ pageSize: z5.coerce.number().int().gte(1).lte(10).default(5)
659
+ };
1072
660
  server.tool(
1073
661
  "get-workouts",
1074
662
  "Get a paginated list of workouts. Returns workout details including title, description, start/end times, and exercises performed. Results are ordered from newest to oldest.",
1075
- {
1076
- page: z41.coerce.number().gte(1).default(1),
1077
- pageSize: z41.coerce.number().int().gte(1).lte(10).default(5)
1078
- },
1079
- withErrorHandling(async ({ page, pageSize }) => {
663
+ getWorkoutsSchema,
664
+ withErrorHandling(async (args) => {
1080
665
  if (!hevyClient) {
1081
666
  throw new Error(
1082
667
  "API client not initialized. Please provide HEVY_API_KEY."
1083
668
  );
1084
669
  }
670
+ const { page, pageSize } = args;
1085
671
  const data = await hevyClient.getWorkouts({
1086
672
  page,
1087
673
  pageSize
@@ -1095,18 +681,20 @@ function registerWorkoutTools(server, hevyClient) {
1095
681
  return createJsonResponse(workouts);
1096
682
  }, "get-workouts")
1097
683
  );
684
+ const getWorkoutSchema = {
685
+ workoutId: z5.string().min(1)
686
+ };
1098
687
  server.tool(
1099
688
  "get-workout",
1100
689
  "Get complete details of a specific workout by ID. Returns all workout information including title, description, start/end times, and detailed exercise data.",
1101
- {
1102
- workoutId: z41.string().min(1)
1103
- },
1104
- withErrorHandling(async ({ workoutId }) => {
690
+ getWorkoutSchema,
691
+ withErrorHandling(async (args) => {
1105
692
  if (!hevyClient) {
1106
693
  throw new Error(
1107
694
  "API client not initialized. Please provide HEVY_API_KEY."
1108
695
  );
1109
696
  }
697
+ const { workoutId } = args;
1110
698
  const data = await hevyClient.getWorkout(workoutId);
1111
699
  if (!data) {
1112
700
  return createEmptyResponse(`Workout with ID ${workoutId} not found`);
@@ -1130,20 +718,22 @@ function registerWorkoutTools(server, hevyClient) {
1130
718
  return createJsonResponse({ count });
1131
719
  }, "get-workout-count")
1132
720
  );
721
+ const getWorkoutEventsSchema = {
722
+ page: z5.coerce.number().int().gte(1).default(1),
723
+ pageSize: z5.coerce.number().int().gte(1).lte(10).default(5),
724
+ since: z5.string().default("1970-01-01T00:00:00Z")
725
+ };
1133
726
  server.tool(
1134
727
  "get-workout-events",
1135
728
  "Retrieve a paged list of workout events (updates or deletes) since a given date. Events are ordered from newest to oldest. The intention is to allow clients to keep their local cache of workouts up to date without having to fetch the entire list of workouts.",
1136
- {
1137
- page: z41.coerce.number().int().gte(1).default(1),
1138
- pageSize: z41.coerce.number().int().gte(1).lte(10).default(5),
1139
- since: z41.string().default("1970-01-01T00:00:00Z")
1140
- },
1141
- withErrorHandling(async ({ page, pageSize, since }) => {
729
+ getWorkoutEventsSchema,
730
+ withErrorHandling(async (args) => {
1142
731
  if (!hevyClient) {
1143
732
  throw new Error(
1144
733
  "API client not initialized. Please provide HEVY_API_KEY."
1145
734
  );
1146
735
  }
736
+ const { page, pageSize, since } = args;
1147
737
  const data = await hevyClient.getWorkoutEvents({
1148
738
  page,
1149
739
  pageSize,
@@ -1158,117 +748,117 @@ function registerWorkoutTools(server, hevyClient) {
1158
748
  return createJsonResponse(events);
1159
749
  }, "get-workout-events")
1160
750
  );
751
+ const createWorkoutSchema = {
752
+ title: z5.string().min(1),
753
+ description: z5.string().optional().nullable(),
754
+ startTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
755
+ endTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
756
+ isPrivate: z5.boolean().default(false),
757
+ exercises: z5.array(
758
+ z5.object({
759
+ exerciseTemplateId: z5.string().min(1),
760
+ supersetId: z5.coerce.number().nullable().optional(),
761
+ notes: z5.string().optional().nullable(),
762
+ sets: z5.array(
763
+ z5.object({
764
+ type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
765
+ weightKg: z5.coerce.number().optional().nullable(),
766
+ reps: z5.coerce.number().int().optional().nullable(),
767
+ distanceMeters: z5.coerce.number().int().optional().nullable(),
768
+ durationSeconds: z5.coerce.number().int().optional().nullable(),
769
+ rpe: z5.coerce.number().optional().nullable(),
770
+ customMetric: z5.coerce.number().optional().nullable()
771
+ })
772
+ )
773
+ })
774
+ )
775
+ };
1161
776
  server.tool(
1162
777
  "create-workout",
1163
778
  "Create a new workout in your Hevy account. Requires title, start/end times, and at least one exercise with sets. Returns the complete workout details upon successful creation including the newly assigned workout ID.",
1164
- {
1165
- title: z41.string().min(1),
1166
- description: z41.string().optional().nullable(),
1167
- startTime: z41.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
1168
- endTime: z41.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
1169
- isPrivate: z41.boolean().default(false),
1170
- exercises: z41.array(
1171
- z41.object({
1172
- exerciseTemplateId: z41.string().min(1),
1173
- supersetId: z41.coerce.number().nullable().optional(),
1174
- notes: z41.string().optional().nullable(),
1175
- sets: z41.array(
1176
- z41.object({
1177
- type: z41.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
1178
- weightKg: z41.coerce.number().optional().nullable(),
1179
- reps: z41.coerce.number().int().optional().nullable(),
1180
- distanceMeters: z41.coerce.number().int().optional().nullable(),
1181
- durationSeconds: z41.coerce.number().int().optional().nullable(),
1182
- rpe: z41.coerce.number().optional().nullable(),
1183
- customMetric: z41.coerce.number().optional().nullable()
1184
- })
1185
- )
1186
- })
1187
- )
1188
- },
1189
- withErrorHandling(
1190
- async ({
1191
- title,
1192
- description,
1193
- startTime,
1194
- endTime,
1195
- isPrivate,
1196
- exercises
1197
- }) => {
1198
- if (!hevyClient) {
1199
- throw new Error(
1200
- "API client not initialized. Please provide HEVY_API_KEY."
1201
- );
1202
- }
1203
- const requestBody = {
1204
- workout: {
1205
- title,
1206
- description: description || null,
1207
- startTime,
1208
- endTime,
1209
- isPrivate,
1210
- exercises: exercises.map((exercise) => ({
1211
- exerciseTemplateId: exercise.exerciseTemplateId,
1212
- supersetId: exercise.supersetId || null,
779
+ createWorkoutSchema,
780
+ withErrorHandling(async (args) => {
781
+ if (!hevyClient) {
782
+ throw new Error(
783
+ "API client not initialized. Please provide HEVY_API_KEY."
784
+ );
785
+ }
786
+ const { title, description, startTime, endTime, isPrivate, exercises } = args;
787
+ const requestBody = {
788
+ workout: {
789
+ title,
790
+ description: description || null,
791
+ start_time: startTime,
792
+ end_time: endTime,
793
+ is_private: isPrivate,
794
+ exercises: exercises.map(
795
+ (exercise) => ({
796
+ exercise_template_id: exercise.exerciseTemplateId,
797
+ superset_id: exercise.supersetId || null,
1213
798
  notes: exercise.notes || null,
1214
799
  sets: exercise.sets.map((set) => ({
1215
800
  type: set.type,
1216
- weightKg: set.weightKg || null,
801
+ weight_kg: set.weightKg || null,
1217
802
  reps: set.reps || null,
1218
- distanceMeters: set.distanceMeters || null,
1219
- durationSeconds: set.durationSeconds || null,
803
+ distance_meters: set.distanceMeters || null,
804
+ duration_seconds: set.durationSeconds || null,
1220
805
  rpe: set.rpe || null,
1221
- customMetric: set.customMetric || null
806
+ custom_metric: set.customMetric || null
1222
807
  }))
1223
- }))
1224
- }
1225
- };
1226
- const data = await hevyClient.createWorkout(requestBody);
1227
- if (!data) {
1228
- return createEmptyResponse(
1229
- "Failed to create workout: Server returned no data"
1230
- );
808
+ })
809
+ )
1231
810
  }
1232
- const workout = formatWorkout(data);
1233
- return createJsonResponse(workout, {
1234
- pretty: true,
1235
- indent: 2
1236
- });
1237
- },
1238
- "create-workout"
1239
- )
811
+ };
812
+ const data = await hevyClient.createWorkout(requestBody);
813
+ if (!data) {
814
+ return createEmptyResponse(
815
+ "Failed to create workout: Server returned no data"
816
+ );
817
+ }
818
+ const workout = formatWorkout(data);
819
+ return createJsonResponse(workout, {
820
+ pretty: true,
821
+ indent: 2
822
+ });
823
+ }, "create-workout")
1240
824
  );
825
+ const updateWorkoutSchema = {
826
+ workoutId: z5.string().min(1),
827
+ title: z5.string().min(1),
828
+ description: z5.string().optional().nullable(),
829
+ startTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
830
+ endTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
831
+ isPrivate: z5.boolean().default(false),
832
+ exercises: z5.array(
833
+ z5.object({
834
+ exerciseTemplateId: z5.string().min(1),
835
+ supersetId: z5.coerce.number().nullable().optional(),
836
+ notes: z5.string().optional().nullable(),
837
+ sets: z5.array(
838
+ z5.object({
839
+ type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
840
+ weightKg: z5.coerce.number().optional().nullable(),
841
+ reps: z5.coerce.number().int().optional().nullable(),
842
+ distanceMeters: z5.coerce.number().int().optional().nullable(),
843
+ durationSeconds: z5.coerce.number().int().optional().nullable(),
844
+ rpe: z5.coerce.number().optional().nullable(),
845
+ customMetric: z5.coerce.number().optional().nullable()
846
+ })
847
+ )
848
+ })
849
+ )
850
+ };
1241
851
  server.tool(
1242
852
  "update-workout",
1243
853
  "Update an existing workout by ID. You can modify the title, description, start/end times, privacy setting, and exercise data. Returns the updated workout with all changes applied.",
1244
- {
1245
- workoutId: z41.string().min(1),
1246
- title: z41.string().min(1),
1247
- description: z41.string().optional().nullable(),
1248
- startTime: z41.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
1249
- endTime: z41.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
1250
- isPrivate: z41.boolean().default(false),
1251
- exercises: z41.array(
1252
- z41.object({
1253
- exerciseTemplateId: z41.string().min(1),
1254
- supersetId: z41.coerce.number().nullable().optional(),
1255
- notes: z41.string().optional().nullable(),
1256
- sets: z41.array(
1257
- z41.object({
1258
- type: z41.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
1259
- weightKg: z41.coerce.number().optional().nullable(),
1260
- reps: z41.coerce.number().int().optional().nullable(),
1261
- distanceMeters: z41.coerce.number().int().optional().nullable(),
1262
- durationSeconds: z41.coerce.number().int().optional().nullable(),
1263
- rpe: z41.coerce.number().optional().nullable(),
1264
- customMetric: z41.coerce.number().optional().nullable()
1265
- })
1266
- )
1267
- })
1268
- )
1269
- },
1270
- withErrorHandling(
1271
- async ({
854
+ updateWorkoutSchema,
855
+ withErrorHandling(async (args) => {
856
+ if (!hevyClient) {
857
+ throw new Error(
858
+ "API client not initialized. Please provide HEVY_API_KEY."
859
+ );
860
+ }
861
+ const {
1272
862
  workoutId,
1273
863
  title,
1274
864
  description,
@@ -1276,44 +866,44 @@ function registerWorkoutTools(server, hevyClient) {
1276
866
  endTime,
1277
867
  isPrivate,
1278
868
  exercises
1279
- }) => {
1280
- const requestBody = {
1281
- workout: {
1282
- title,
1283
- description: description || null,
1284
- startTime,
1285
- endTime,
1286
- isPrivate,
1287
- exercises: exercises.map((exercise) => ({
1288
- exerciseTemplateId: exercise.exerciseTemplateId,
1289
- supersetId: exercise.supersetId || null,
869
+ } = args;
870
+ const requestBody = {
871
+ workout: {
872
+ title,
873
+ description: description || null,
874
+ start_time: startTime,
875
+ end_time: endTime,
876
+ is_private: isPrivate,
877
+ exercises: exercises.map(
878
+ (exercise) => ({
879
+ exercise_template_id: exercise.exerciseTemplateId,
880
+ superset_id: exercise.supersetId || null,
1290
881
  notes: exercise.notes || null,
1291
882
  sets: exercise.sets.map((set) => ({
1292
883
  type: set.type,
1293
- weightKg: set.weightKg || null,
884
+ weight_kg: set.weightKg || null,
1294
885
  reps: set.reps || null,
1295
- distanceMeters: set.distanceMeters || null,
1296
- durationSeconds: set.durationSeconds || null,
886
+ distance_meters: set.distanceMeters || null,
887
+ duration_seconds: set.durationSeconds || null,
1297
888
  rpe: set.rpe || null,
1298
- customMetric: set.customMetric || null
889
+ custom_metric: set.customMetric || null
1299
890
  }))
1300
- }))
1301
- }
1302
- };
1303
- const data = await hevyClient.updateWorkout(workoutId, requestBody);
1304
- if (!data) {
1305
- return createEmptyResponse(
1306
- `Failed to update workout with ID ${workoutId}`
1307
- );
891
+ })
892
+ )
1308
893
  }
1309
- const workout = formatWorkout(data);
1310
- return createJsonResponse(workout, {
1311
- pretty: true,
1312
- indent: 2
1313
- });
1314
- },
1315
- "update-workout-operation"
1316
- )
894
+ };
895
+ const data = await hevyClient.updateWorkout(workoutId, requestBody);
896
+ if (!data) {
897
+ return createEmptyResponse(
898
+ `Failed to update workout with ID ${workoutId}`
899
+ );
900
+ }
901
+ const workout = formatWorkout(data);
902
+ return createJsonResponse(workout, {
903
+ pretty: true,
904
+ indent: 2
905
+ });
906
+ }, "update-workout-operation")
1317
907
  );
1318
908
  }
1319
909
 
@@ -1631,8 +1221,8 @@ function createClient2(apiKey, baseUrl) {
1631
1221
  // src/index.ts
1632
1222
  dotenvx.config({ quiet: true });
1633
1223
  var HEVY_API_BASEURL = "https://api.hevyapp.com";
1634
- var serverConfigSchema = z42.object({
1635
- apiKey: z42.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).")
1224
+ var serverConfigSchema = z6.object({
1225
+ apiKey: z6.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).")
1636
1226
  });
1637
1227
  function buildServer(apiKey) {
1638
1228
  const server = new McpServer({