hevy-mcp 1.12.12 → 1.12.13

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 ADDED
@@ -0,0 +1,1667 @@
1
+ #!/usr/bin/env node
2
+ // Generated with tsup
3
+ // https://github.com/egoist/tsup
4
+
5
+ // src/index.ts
6
+ import dotenvx from "@dotenvx/dotenvx";
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { z as z42 } from "zod";
10
+
11
+ // package.json
12
+ var name = "hevy-mcp";
13
+ var version = "1.12.12";
14
+
15
+ // src/tools/folders.ts
16
+ import { z } from "zod";
17
+
18
+ // src/utils/error-handler.ts
19
+ function createErrorResponse(error, context) {
20
+ const errorMessage = error instanceof Error ? error.message : String(error);
21
+ const errorCode = error instanceof Error && "code" in error ? error.code : void 0;
22
+ const errorType = determineErrorType(error, errorMessage);
23
+ if (errorCode) {
24
+ console.debug(`Error code: ${errorCode}`);
25
+ }
26
+ const contextPrefix = context ? `[${context}] ` : "";
27
+ const formattedMessage = `${contextPrefix}Error: ${errorMessage}`;
28
+ console.error(`${formattedMessage} (Type: ${errorType})`, error);
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: formattedMessage
34
+ }
35
+ ],
36
+ isError: true
37
+ };
38
+ }
39
+ function determineErrorType(error, message) {
40
+ const messageLower = message.toLowerCase();
41
+ const nameLower = error instanceof Error ? error.name.toLowerCase() : "";
42
+ if (nameLower.includes("network") || messageLower.includes("network") || nameLower.includes("fetch") || messageLower.includes("fetch") || nameLower.includes("timeout") || messageLower.includes("timeout")) {
43
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
44
+ }
45
+ if (nameLower.includes("validation") || messageLower.includes("validation") || messageLower.includes("invalid") || messageLower.includes("required")) {
46
+ return "VALIDATION_ERROR" /* VALIDATION_ERROR */;
47
+ }
48
+ if (messageLower.includes("not found") || messageLower.includes("404") || messageLower.includes("does not exist")) {
49
+ return "NOT_FOUND" /* NOT_FOUND */;
50
+ }
51
+ if (nameLower.includes("api") || messageLower.includes("api") || messageLower.includes("server error") || messageLower.includes("500")) {
52
+ return "API_ERROR" /* API_ERROR */;
53
+ }
54
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
55
+ }
56
+ function withErrorHandling(fn, context) {
57
+ return (async (...args) => {
58
+ try {
59
+ return await fn(...args);
60
+ } catch (error) {
61
+ return createErrorResponse(error, context);
62
+ }
63
+ });
64
+ }
65
+
66
+ // src/utils/formatters.ts
67
+ function formatWorkout(workout) {
68
+ return {
69
+ id: workout.id,
70
+ title: workout.title,
71
+ description: workout.description,
72
+ startTime: workout.start_time,
73
+ endTime: workout.end_time,
74
+ createdAt: workout.created_at,
75
+ updatedAt: workout.updated_at,
76
+ duration: calculateDuration(workout.start_time, workout.end_time),
77
+ exercises: workout.exercises?.map((exercise) => {
78
+ return {
79
+ index: exercise.index,
80
+ name: exercise.title,
81
+ exerciseTemplateId: exercise.exercise_template_id,
82
+ notes: exercise.notes,
83
+ supersetsId: exercise.supersets_id,
84
+ sets: exercise.sets?.map((set) => ({
85
+ index: set.index,
86
+ type: set.type,
87
+ weight: set.weight_kg,
88
+ reps: set.reps,
89
+ distance: set.distance_meters,
90
+ duration: set.duration_seconds,
91
+ rpe: set.rpe,
92
+ customMetric: set.custom_metric
93
+ }))
94
+ };
95
+ })
96
+ };
97
+ }
98
+ function formatRoutine(routine) {
99
+ return {
100
+ id: routine.id,
101
+ title: routine.title,
102
+ folderId: routine.folder_id,
103
+ createdAt: routine.created_at,
104
+ updatedAt: routine.updated_at,
105
+ exercises: routine.exercises?.map((exercise) => {
106
+ return {
107
+ name: exercise.title,
108
+ index: exercise.index,
109
+ exerciseTemplateId: exercise.exercise_template_id,
110
+ notes: exercise.notes,
111
+ supersetId: exercise.supersets_id,
112
+ restSeconds: exercise.rest_seconds,
113
+ sets: exercise.sets?.map((set) => ({
114
+ index: set.index,
115
+ type: set.type,
116
+ weight: set.weight_kg,
117
+ reps: set.reps,
118
+ ...set.rep_range !== void 0 && { repRange: set.rep_range },
119
+ distance: set.distance_meters,
120
+ duration: set.duration_seconds,
121
+ ...set.rpe !== void 0 && { rpe: set.rpe },
122
+ customMetric: set.custom_metric
123
+ }))
124
+ };
125
+ })
126
+ };
127
+ }
128
+ function formatRoutineFolder(folder) {
129
+ return {
130
+ id: folder.id,
131
+ title: folder.title,
132
+ createdAt: folder.created_at,
133
+ updatedAt: folder.updated_at
134
+ };
135
+ }
136
+ function calculateDuration(startTime, endTime) {
137
+ if (!startTime || !endTime) return "Unknown duration";
138
+ try {
139
+ const start = new Date(startTime);
140
+ const end = new Date(endTime);
141
+ if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
142
+ return "Unknown duration";
143
+ }
144
+ const durationMs = end.getTime() - start.getTime();
145
+ if (durationMs < 0) {
146
+ return "Invalid duration (end time before start time)";
147
+ }
148
+ const hours = Math.floor(durationMs / (1e3 * 60 * 60));
149
+ const minutes = Math.floor(durationMs % (1e3 * 60 * 60) / (1e3 * 60));
150
+ const seconds = Math.floor(durationMs % (1e3 * 60) / 1e3);
151
+ return `${hours}h ${minutes}m ${seconds}s`;
152
+ } catch (error) {
153
+ console.error("Error calculating duration:", error);
154
+ return "Unknown duration";
155
+ }
156
+ }
157
+ function formatExerciseTemplate(template) {
158
+ return {
159
+ id: template.id,
160
+ title: template.title,
161
+ type: template.type,
162
+ primaryMuscleGroup: template.primary_muscle_group,
163
+ secondaryMuscleGroups: template.secondary_muscle_groups,
164
+ isCustom: template.is_custom
165
+ };
166
+ }
167
+
168
+ // src/utils/response-formatter.ts
169
+ function createJsonResponse(data, options = { pretty: true, indent: 2 }) {
170
+ const jsonString = options.pretty ? JSON.stringify(data, null, options.indent) : JSON.stringify(data);
171
+ return {
172
+ content: [
173
+ {
174
+ type: "text",
175
+ text: jsonString
176
+ }
177
+ ]
178
+ };
179
+ }
180
+ function createEmptyResponse(message = "No data found") {
181
+ return {
182
+ content: [
183
+ {
184
+ type: "text",
185
+ text: message
186
+ }
187
+ ]
188
+ };
189
+ }
190
+
191
+ // src/tools/folders.ts
192
+ function registerFolderTools(server, hevyClient) {
193
+ server.tool(
194
+ "get-routine-folders",
195
+ "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
+ )
223
+ );
224
+ server.tool(
225
+ "get-routine-folder",
226
+ "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 }) => {
231
+ if (!hevyClient) {
232
+ throw new Error(
233
+ "API client not initialized. Please provide HEVY_API_KEY."
234
+ );
235
+ }
236
+ const data = await hevyClient.getRoutineFolder(folderId);
237
+ if (!data) {
238
+ return createEmptyResponse(
239
+ `Routine folder with ID ${folderId} not found`
240
+ );
241
+ }
242
+ const folder = formatRoutineFolder(data);
243
+ return createJsonResponse(folder);
244
+ }, "get-routine-folder")
245
+ );
246
+ server.tool(
247
+ "create-routine-folder",
248
+ "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
+ if (!hevyClient) {
254
+ throw new Error(
255
+ "API client not initialized. Please provide HEVY_API_KEY."
256
+ );
257
+ }
258
+ const data = await hevyClient.createRoutineFolder({
259
+ routine_folder: {
260
+ title: name2
261
+ }
262
+ });
263
+ if (!data) {
264
+ return createEmptyResponse(
265
+ "Failed to create routine folder: Server returned no data"
266
+ );
267
+ }
268
+ const folder = formatRoutineFolder(data);
269
+ return createJsonResponse(folder, {
270
+ pretty: true,
271
+ indent: 2
272
+ });
273
+ }, "create-routine-folder")
274
+ );
275
+ }
276
+
277
+ // src/tools/routines.ts
278
+ import { z as z2 } from "zod";
279
+ function registerRoutineTools(server, hevyClient) {
280
+ server.tool(
281
+ "get-routines",
282
+ "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
+ },
287
+ withErrorHandling(async (args) => {
288
+ if (!hevyClient) {
289
+ throw new Error(
290
+ "API client not initialized. Please provide HEVY_API_KEY."
291
+ );
292
+ }
293
+ const { page, pageSize } = args;
294
+ const data = await hevyClient.getRoutines({
295
+ page,
296
+ pageSize
297
+ });
298
+ const routines = data?.routines?.map((routine) => formatRoutine(routine)) || [];
299
+ if (routines.length === 0) {
300
+ return createEmptyResponse(
301
+ "No routines found for the specified parameters"
302
+ );
303
+ }
304
+ return createJsonResponse(routines);
305
+ }, "get-routines")
306
+ );
307
+ server.tool(
308
+ "get-routine",
309
+ "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 }) => {
314
+ if (!hevyClient) {
315
+ throw new Error(
316
+ "API client not initialized. Please provide HEVY_API_KEY."
317
+ );
318
+ }
319
+ const data = await hevyClient.getRoutineById(String(routineId));
320
+ if (!data || !data.routine) {
321
+ return createEmptyResponse(`Routine with ID ${routineId} not found`);
322
+ }
323
+ const routine = formatRoutine(data.routine);
324
+ return createJsonResponse(routine);
325
+ }, "get-routine")
326
+ );
327
+ server.tool(
328
+ "create-routine",
329
+ "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
+ },
353
+ withErrorHandling(async (args) => {
354
+ if (!hevyClient) {
355
+ throw new Error(
356
+ "API client not initialized. Please provide HEVY_API_KEY."
357
+ );
358
+ }
359
+ const { title, folderId, notes, exercises } = args;
360
+ const data = await hevyClient.createRoutine({
361
+ routine: {
362
+ title,
363
+ folder_id: folderId ?? null,
364
+ notes: notes ?? "",
365
+ exercises: exercises.map(
366
+ (exercise) => ({
367
+ exercise_template_id: exercise.exerciseTemplateId,
368
+ superset_id: exercise.supersetId ?? null,
369
+ rest_seconds: exercise.restSeconds ?? null,
370
+ notes: exercise.notes ?? null,
371
+ sets: exercise.sets.map(
372
+ (set) => ({
373
+ type: set.type,
374
+ weight_kg: set.weightKg ?? null,
375
+ reps: set.reps ?? null,
376
+ distance_meters: set.distanceMeters ?? null,
377
+ duration_seconds: set.durationSeconds ?? null,
378
+ custom_metric: set.customMetric ?? null
379
+ })
380
+ )
381
+ })
382
+ )
383
+ }
384
+ });
385
+ if (!data) {
386
+ return createEmptyResponse(
387
+ "Failed to create routine: Server returned no data"
388
+ );
389
+ }
390
+ const routine = formatRoutine(data);
391
+ return createJsonResponse(routine, {
392
+ pretty: true,
393
+ indent: 2
394
+ });
395
+ }, "create-routine")
396
+ );
397
+ server.tool(
398
+ "update-routine",
399
+ "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
+ },
423
+ withErrorHandling(async (args) => {
424
+ if (!hevyClient) {
425
+ throw new Error(
426
+ "API client not initialized. Please provide HEVY_API_KEY."
427
+ );
428
+ }
429
+ const { routineId, title, notes, exercises } = args;
430
+ const data = await hevyClient.updateRoutine(routineId, {
431
+ routine: {
432
+ title,
433
+ notes: notes ?? null,
434
+ exercises: exercises.map(
435
+ (exercise) => ({
436
+ exercise_template_id: exercise.exerciseTemplateId,
437
+ superset_id: exercise.supersetId ?? null,
438
+ rest_seconds: exercise.restSeconds ?? null,
439
+ notes: exercise.notes ?? null,
440
+ sets: exercise.sets.map(
441
+ (set) => ({
442
+ type: set.type,
443
+ weight_kg: set.weightKg ?? null,
444
+ reps: set.reps ?? null,
445
+ distance_meters: set.distanceMeters ?? null,
446
+ duration_seconds: set.durationSeconds ?? null,
447
+ custom_metric: set.customMetric ?? null
448
+ })
449
+ )
450
+ })
451
+ )
452
+ }
453
+ });
454
+ if (!data) {
455
+ return createEmptyResponse(
456
+ `Failed to update routine with ID ${routineId}`
457
+ );
458
+ }
459
+ const routine = formatRoutine(data);
460
+ return createJsonResponse(routine, {
461
+ pretty: true,
462
+ indent: 2
463
+ });
464
+ }, "update-routine")
465
+ );
466
+ }
467
+
468
+ // src/tools/templates.ts
469
+ import { z as z3 } from "zod";
470
+ function registerTemplateTools(server, hevyClient) {
471
+ server.tool(
472
+ "get-exercise-templates",
473
+ "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
+ )
501
+ );
502
+ server.tool(
503
+ "get-exercise-template",
504
+ "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
+ )
526
+ );
527
+ }
528
+
529
+ // src/tools/webhooks.ts
530
+ import { z as z40 } from "zod";
531
+
532
+ // src/generated/client/schemas/deletedWorkoutSchema.ts
533
+ 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(
974
+ (url) => {
975
+ try {
976
+ const parsed = new URL(url);
977
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
978
+ } catch {
979
+ return false;
980
+ }
981
+ },
982
+ {
983
+ message: "Webhook URL must be a valid HTTP or HTTPS URL"
984
+ }
985
+ ).refine(
986
+ (url) => {
987
+ try {
988
+ const parsed = new URL(url);
989
+ return parsed.hostname !== "localhost" && !parsed.hostname.startsWith("127.");
990
+ } catch {
991
+ return false;
992
+ }
993
+ },
994
+ {
995
+ message: "Webhook URL cannot be localhost or loopback address"
996
+ }
997
+ );
998
+ function registerWebhookTools(server, hevyClient) {
999
+ server.tool(
1000
+ "get-webhook-subscription",
1001
+ "Get the current webhook subscription for this account. Returns the webhook URL and auth token if a subscription exists.",
1002
+ {},
1003
+ withErrorHandling(async () => {
1004
+ if (!hevyClient) {
1005
+ throw new Error(
1006
+ "API client not initialized. Please provide HEVY_API_KEY."
1007
+ );
1008
+ }
1009
+ const data = await hevyClient.getWebhookSubscription();
1010
+ if (!data) {
1011
+ return createEmptyResponse(
1012
+ "No webhook subscription found for this account"
1013
+ );
1014
+ }
1015
+ return createJsonResponse(data);
1016
+ }, "get-webhook-subscription")
1017
+ );
1018
+ server.tool(
1019
+ "create-webhook-subscription",
1020
+ "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 }) => {
1030
+ if (!hevyClient) {
1031
+ throw new Error(
1032
+ "API client not initialized. Please provide HEVY_API_KEY."
1033
+ );
1034
+ }
1035
+ const requestBody = webhookRequestBodySchema.parse({
1036
+ url,
1037
+ authToken
1038
+ });
1039
+ const data = await hevyClient.createWebhookSubscription(requestBody);
1040
+ if (!data) {
1041
+ return createEmptyResponse(
1042
+ "Failed to create webhook subscription - please check your URL and try again"
1043
+ );
1044
+ }
1045
+ return createJsonResponse(data);
1046
+ }, "create-webhook-subscription")
1047
+ );
1048
+ server.tool(
1049
+ "delete-webhook-subscription",
1050
+ "Delete the current webhook subscription for this account. This will stop all webhook notifications.",
1051
+ {},
1052
+ withErrorHandling(async () => {
1053
+ if (!hevyClient) {
1054
+ throw new Error(
1055
+ "API client not initialized. Please provide HEVY_API_KEY."
1056
+ );
1057
+ }
1058
+ const data = await hevyClient.deleteWebhookSubscription();
1059
+ if (!data) {
1060
+ return createEmptyResponse(
1061
+ "Failed to delete webhook subscription - no subscription may exist or there was a server error"
1062
+ );
1063
+ }
1064
+ return createJsonResponse(data);
1065
+ }, "delete-webhook-subscription")
1066
+ );
1067
+ }
1068
+
1069
+ // src/tools/workouts.ts
1070
+ import { z as z41 } from "zod";
1071
+ function registerWorkoutTools(server, hevyClient) {
1072
+ server.tool(
1073
+ "get-workouts",
1074
+ "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 }) => {
1080
+ if (!hevyClient) {
1081
+ throw new Error(
1082
+ "API client not initialized. Please provide HEVY_API_KEY."
1083
+ );
1084
+ }
1085
+ const data = await hevyClient.getWorkouts({
1086
+ page,
1087
+ pageSize
1088
+ });
1089
+ const workouts = data?.workouts?.map((workout) => formatWorkout(workout)) || [];
1090
+ if (workouts.length === 0) {
1091
+ return createEmptyResponse(
1092
+ "No workouts found for the specified parameters"
1093
+ );
1094
+ }
1095
+ return createJsonResponse(workouts);
1096
+ }, "get-workouts")
1097
+ );
1098
+ server.tool(
1099
+ "get-workout",
1100
+ "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 }) => {
1105
+ if (!hevyClient) {
1106
+ throw new Error(
1107
+ "API client not initialized. Please provide HEVY_API_KEY."
1108
+ );
1109
+ }
1110
+ const data = await hevyClient.getWorkout(workoutId);
1111
+ if (!data) {
1112
+ return createEmptyResponse(`Workout with ID ${workoutId} not found`);
1113
+ }
1114
+ const workout = formatWorkout(data);
1115
+ return createJsonResponse(workout);
1116
+ }, "get-workout")
1117
+ );
1118
+ server.tool(
1119
+ "get-workout-count",
1120
+ "Get the total number of workouts on the account. Useful for pagination or statistics.",
1121
+ {},
1122
+ withErrorHandling(async () => {
1123
+ if (!hevyClient) {
1124
+ throw new Error(
1125
+ "API client not initialized. Please provide HEVY_API_KEY."
1126
+ );
1127
+ }
1128
+ const data = await hevyClient.getWorkoutCount();
1129
+ const count = data ? data.workoutCount || 0 : 0;
1130
+ return createJsonResponse({ count });
1131
+ }, "get-workout-count")
1132
+ );
1133
+ server.tool(
1134
+ "get-workout-events",
1135
+ "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 }) => {
1142
+ if (!hevyClient) {
1143
+ throw new Error(
1144
+ "API client not initialized. Please provide HEVY_API_KEY."
1145
+ );
1146
+ }
1147
+ const data = await hevyClient.getWorkoutEvents({
1148
+ page,
1149
+ pageSize,
1150
+ since
1151
+ });
1152
+ const events = data?.events || [];
1153
+ if (events.length === 0) {
1154
+ return createEmptyResponse(
1155
+ `No workout events found for the specified parameters since ${since}`
1156
+ );
1157
+ }
1158
+ return createJsonResponse(events);
1159
+ }, "get-workout-events")
1160
+ );
1161
+ server.tool(
1162
+ "create-workout",
1163
+ "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,
1213
+ notes: exercise.notes || null,
1214
+ sets: exercise.sets.map((set) => ({
1215
+ type: set.type,
1216
+ weightKg: set.weightKg || null,
1217
+ reps: set.reps || null,
1218
+ distanceMeters: set.distanceMeters || null,
1219
+ durationSeconds: set.durationSeconds || null,
1220
+ rpe: set.rpe || null,
1221
+ customMetric: set.customMetric || null
1222
+ }))
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
+ );
1231
+ }
1232
+ const workout = formatWorkout(data);
1233
+ return createJsonResponse(workout, {
1234
+ pretty: true,
1235
+ indent: 2
1236
+ });
1237
+ },
1238
+ "create-workout"
1239
+ )
1240
+ );
1241
+ server.tool(
1242
+ "update-workout",
1243
+ "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 ({
1272
+ workoutId,
1273
+ title,
1274
+ description,
1275
+ startTime,
1276
+ endTime,
1277
+ isPrivate,
1278
+ 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,
1290
+ notes: exercise.notes || null,
1291
+ sets: exercise.sets.map((set) => ({
1292
+ type: set.type,
1293
+ weightKg: set.weightKg || null,
1294
+ reps: set.reps || null,
1295
+ distanceMeters: set.distanceMeters || null,
1296
+ durationSeconds: set.durationSeconds || null,
1297
+ rpe: set.rpe || null,
1298
+ customMetric: set.customMetric || null
1299
+ }))
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
+ );
1308
+ }
1309
+ const workout = formatWorkout(data);
1310
+ return createJsonResponse(workout, {
1311
+ pretty: true,
1312
+ indent: 2
1313
+ });
1314
+ },
1315
+ "update-workout-operation"
1316
+ )
1317
+ );
1318
+ }
1319
+
1320
+ // src/utils/config.ts
1321
+ function parseConfig(argv, env) {
1322
+ let apiKey = "";
1323
+ const apiKeyArgPatterns = [
1324
+ /^--hevy-api-key=(.+)$/i,
1325
+ /^--hevyApiKey=(.+)$/i,
1326
+ /^hevy-api-key=(.+)$/i
1327
+ ];
1328
+ for (const raw of argv) {
1329
+ for (const pattern of apiKeyArgPatterns) {
1330
+ const m = raw.match(pattern);
1331
+ if (m) {
1332
+ apiKey = m[1];
1333
+ break;
1334
+ }
1335
+ }
1336
+ if (apiKey) break;
1337
+ }
1338
+ if (!apiKey) {
1339
+ apiKey = env.HEVY_API_KEY || "";
1340
+ }
1341
+ return {
1342
+ apiKey
1343
+ };
1344
+ }
1345
+ function assertApiKey(apiKey) {
1346
+ if (!apiKey) {
1347
+ console.error(
1348
+ "Hevy API key is required. Provide it via the HEVY_API_KEY environment variable or the --hevy-api-key=YOUR_KEY command argument."
1349
+ );
1350
+ process.exit(1);
1351
+ }
1352
+ }
1353
+
1354
+ // src/utils/hevyClientKubb.ts
1355
+ import axios from "axios";
1356
+
1357
+ // src/generated/client/api/deleteV1WebhookSubscription.ts
1358
+ import fetch from "@kubb/plugin-client/clients/axios";
1359
+ function getDeleteV1WebhookSubscriptionUrl() {
1360
+ const res = { method: "DELETE", url: `/v1/webhook-subscription` };
1361
+ return res;
1362
+ }
1363
+ async function deleteV1WebhookSubscription(headers, config = {}) {
1364
+ const { client: request = fetch, ...requestConfig } = config;
1365
+ const res = await request({ method: "DELETE", url: getDeleteV1WebhookSubscriptionUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1366
+ return res.data;
1367
+ }
1368
+
1369
+ // src/generated/client/api/getV1ExerciseTemplates.ts
1370
+ import fetch2 from "@kubb/plugin-client/clients/axios";
1371
+ function getGetV1ExerciseTemplatesUrl() {
1372
+ const res = { method: "GET", url: `/v1/exercise_templates` };
1373
+ return res;
1374
+ }
1375
+ async function getV1ExerciseTemplates(headers, params, config = {}) {
1376
+ const { client: request = fetch2, ...requestConfig } = config;
1377
+ const res = await request({ method: "GET", url: getGetV1ExerciseTemplatesUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1378
+ return res.data;
1379
+ }
1380
+
1381
+ // src/generated/client/api/getV1ExerciseTemplatesExercisetemplateid.ts
1382
+ import fetch3 from "@kubb/plugin-client/clients/axios";
1383
+ function getGetV1ExerciseTemplatesExercisetemplateidUrl(exerciseTemplateId) {
1384
+ const res = { method: "GET", url: `/v1/exercise_templates/${exerciseTemplateId}` };
1385
+ return res;
1386
+ }
1387
+ async function getV1ExerciseTemplatesExercisetemplateid(exerciseTemplateId, headers, config = {}) {
1388
+ const { client: request = fetch3, ...requestConfig } = config;
1389
+ const res = await request({ method: "GET", url: getGetV1ExerciseTemplatesExercisetemplateidUrl(exerciseTemplateId).url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1390
+ return res.data;
1391
+ }
1392
+
1393
+ // src/generated/client/api/getV1RoutineFolders.ts
1394
+ import fetch4 from "@kubb/plugin-client/clients/axios";
1395
+ function getGetV1RoutineFoldersUrl() {
1396
+ const res = { method: "GET", url: `/v1/routine_folders` };
1397
+ return res;
1398
+ }
1399
+ async function getV1RoutineFolders(headers, params, config = {}) {
1400
+ const { client: request = fetch4, ...requestConfig } = config;
1401
+ const res = await request({ method: "GET", url: getGetV1RoutineFoldersUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1402
+ return res.data;
1403
+ }
1404
+
1405
+ // src/generated/client/api/getV1RoutineFoldersFolderid.ts
1406
+ import fetch5 from "@kubb/plugin-client/clients/axios";
1407
+ function getGetV1RoutineFoldersFolderidUrl(folderId) {
1408
+ const res = { method: "GET", url: `/v1/routine_folders/${folderId}` };
1409
+ return res;
1410
+ }
1411
+ async function getV1RoutineFoldersFolderid(folderId, headers, config = {}) {
1412
+ const { client: request = fetch5, ...requestConfig } = config;
1413
+ const res = await request({ method: "GET", url: getGetV1RoutineFoldersFolderidUrl(folderId).url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1414
+ return res.data;
1415
+ }
1416
+
1417
+ // src/generated/client/api/getV1Routines.ts
1418
+ import fetch6 from "@kubb/plugin-client/clients/axios";
1419
+ function getGetV1RoutinesUrl() {
1420
+ const res = { method: "GET", url: `/v1/routines` };
1421
+ return res;
1422
+ }
1423
+ async function getV1Routines(headers, params, config = {}) {
1424
+ const { client: request = fetch6, ...requestConfig } = config;
1425
+ const res = await request({ method: "GET", url: getGetV1RoutinesUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1426
+ return res.data;
1427
+ }
1428
+
1429
+ // src/generated/client/api/getV1RoutinesRoutineid.ts
1430
+ import fetch7 from "@kubb/plugin-client/clients/axios";
1431
+ function getGetV1RoutinesRoutineidUrl(routineId) {
1432
+ const res = { method: "GET", url: `/v1/routines/${routineId}` };
1433
+ return res;
1434
+ }
1435
+ async function getV1RoutinesRoutineid(routineId, headers, config = {}) {
1436
+ const { client: request = fetch7, ...requestConfig } = config;
1437
+ const res = await request({ method: "GET", url: getGetV1RoutinesRoutineidUrl(routineId).url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1438
+ return res.data;
1439
+ }
1440
+
1441
+ // src/generated/client/api/getV1WebhookSubscription.ts
1442
+ import fetch8 from "@kubb/plugin-client/clients/axios";
1443
+ function getGetV1WebhookSubscriptionUrl() {
1444
+ const res = { method: "GET", url: `/v1/webhook-subscription` };
1445
+ return res;
1446
+ }
1447
+ async function getV1WebhookSubscription(headers, config = {}) {
1448
+ const { client: request = fetch8, ...requestConfig } = config;
1449
+ const res = await request({ method: "GET", url: getGetV1WebhookSubscriptionUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1450
+ return res.data;
1451
+ }
1452
+
1453
+ // src/generated/client/api/getV1Workouts.ts
1454
+ import fetch9 from "@kubb/plugin-client/clients/axios";
1455
+ function getGetV1WorkoutsUrl() {
1456
+ const res = { method: "GET", url: `/v1/workouts` };
1457
+ return res;
1458
+ }
1459
+ async function getV1Workouts(headers, params, config = {}) {
1460
+ const { client: request = fetch9, ...requestConfig } = config;
1461
+ const res = await request({ method: "GET", url: getGetV1WorkoutsUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1462
+ return res.data;
1463
+ }
1464
+
1465
+ // src/generated/client/api/getV1WorkoutsCount.ts
1466
+ import fetch10 from "@kubb/plugin-client/clients/axios";
1467
+ function getGetV1WorkoutsCountUrl() {
1468
+ const res = { method: "GET", url: `/v1/workouts/count` };
1469
+ return res;
1470
+ }
1471
+ async function getV1WorkoutsCount(headers, config = {}) {
1472
+ const { client: request = fetch10, ...requestConfig } = config;
1473
+ const res = await request({ method: "GET", url: getGetV1WorkoutsCountUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1474
+ return res.data;
1475
+ }
1476
+
1477
+ // src/generated/client/api/getV1WorkoutsEvents.ts
1478
+ import fetch11 from "@kubb/plugin-client/clients/axios";
1479
+ function getGetV1WorkoutsEventsUrl() {
1480
+ const res = { method: "GET", url: `/v1/workouts/events` };
1481
+ return res;
1482
+ }
1483
+ async function getV1WorkoutsEvents(headers, params, config = {}) {
1484
+ const { client: request = fetch11, ...requestConfig } = config;
1485
+ const res = await request({ method: "GET", url: getGetV1WorkoutsEventsUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1486
+ return res.data;
1487
+ }
1488
+
1489
+ // src/generated/client/api/getV1WorkoutsWorkoutid.ts
1490
+ import fetch12 from "@kubb/plugin-client/clients/axios";
1491
+ function getGetV1WorkoutsWorkoutidUrl(workoutId) {
1492
+ const res = { method: "GET", url: `/v1/workouts/${workoutId}` };
1493
+ return res;
1494
+ }
1495
+ async function getV1WorkoutsWorkoutid(workoutId, headers, config = {}) {
1496
+ const { client: request = fetch12, ...requestConfig } = config;
1497
+ const res = await request({ method: "GET", url: getGetV1WorkoutsWorkoutidUrl(workoutId).url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1498
+ return res.data;
1499
+ }
1500
+
1501
+ // src/generated/client/api/postV1RoutineFolders.ts
1502
+ import fetch13 from "@kubb/plugin-client/clients/axios";
1503
+ function getPostV1RoutineFoldersUrl() {
1504
+ const res = { method: "POST", url: `/v1/routine_folders` };
1505
+ return res;
1506
+ }
1507
+ async function postV1RoutineFolders(headers, data, config = {}) {
1508
+ const { client: request = fetch13, ...requestConfig } = config;
1509
+ const requestData = data;
1510
+ const res = await request({ method: "POST", url: getPostV1RoutineFoldersUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1511
+ return res.data;
1512
+ }
1513
+
1514
+ // src/generated/client/api/postV1Routines.ts
1515
+ import fetch14 from "@kubb/plugin-client/clients/axios";
1516
+ function getPostV1RoutinesUrl() {
1517
+ const res = { method: "POST", url: `/v1/routines` };
1518
+ return res;
1519
+ }
1520
+ async function postV1Routines(headers, data, config = {}) {
1521
+ const { client: request = fetch14, ...requestConfig } = config;
1522
+ const requestData = data;
1523
+ const res = await request({ method: "POST", url: getPostV1RoutinesUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1524
+ return res.data;
1525
+ }
1526
+
1527
+ // src/generated/client/api/postV1WebhookSubscription.ts
1528
+ import fetch15 from "@kubb/plugin-client/clients/axios";
1529
+ function getPostV1WebhookSubscriptionUrl() {
1530
+ const res = { method: "POST", url: `/v1/webhook-subscription` };
1531
+ return res;
1532
+ }
1533
+ async function postV1WebhookSubscription(headers, data, config = {}) {
1534
+ const { client: request = fetch15, ...requestConfig } = config;
1535
+ const requestData = data;
1536
+ const res = await request({ method: "POST", url: getPostV1WebhookSubscriptionUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1537
+ return res.data;
1538
+ }
1539
+
1540
+ // src/generated/client/api/postV1Workouts.ts
1541
+ import fetch16 from "@kubb/plugin-client/clients/axios";
1542
+ function getPostV1WorkoutsUrl() {
1543
+ const res = { method: "POST", url: `/v1/workouts` };
1544
+ return res;
1545
+ }
1546
+ async function postV1Workouts(headers, data, config = {}) {
1547
+ const { client: request = fetch16, ...requestConfig } = config;
1548
+ const requestData = data;
1549
+ const res = await request({ method: "POST", url: getPostV1WorkoutsUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1550
+ return res.data;
1551
+ }
1552
+
1553
+ // src/generated/client/api/putV1RoutinesRoutineid.ts
1554
+ import fetch17 from "@kubb/plugin-client/clients/axios";
1555
+ function getPutV1RoutinesRoutineidUrl(routineId) {
1556
+ const res = { method: "PUT", url: `/v1/routines/${routineId}` };
1557
+ return res;
1558
+ }
1559
+ async function putV1RoutinesRoutineid(routineId, headers, data, config = {}) {
1560
+ const { client: request = fetch17, ...requestConfig } = config;
1561
+ const requestData = data;
1562
+ const res = await request({ method: "PUT", url: getPutV1RoutinesRoutineidUrl(routineId).url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1563
+ return res.data;
1564
+ }
1565
+
1566
+ // src/generated/client/api/putV1WorkoutsWorkoutid.ts
1567
+ import fetch18 from "@kubb/plugin-client/clients/axios";
1568
+ function getPutV1WorkoutsWorkoutidUrl(workoutId) {
1569
+ const res = { method: "PUT", url: `/v1/workouts/${workoutId}` };
1570
+ return res;
1571
+ }
1572
+ async function putV1WorkoutsWorkoutid(workoutId, headers, data, config = {}) {
1573
+ const { client: request = fetch18, ...requestConfig } = config;
1574
+ const requestData = data;
1575
+ const res = await request({ method: "PUT", url: getPutV1WorkoutsWorkoutidUrl(workoutId).url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1576
+ return res.data;
1577
+ }
1578
+
1579
+ // src/utils/hevyClientKubb.ts
1580
+ function createClient(apiKey, baseUrl = "https://api.hevyapp.com") {
1581
+ const axiosInstance = axios.create({
1582
+ baseURL: baseUrl,
1583
+ headers: {
1584
+ "api-key": apiKey
1585
+ }
1586
+ });
1587
+ const headers = {
1588
+ "api-key": apiKey
1589
+ };
1590
+ const client = axiosInstance;
1591
+ return {
1592
+ // Workouts
1593
+ getWorkouts: (params) => getV1Workouts(headers, params, { client }),
1594
+ getWorkout: (workoutId) => getV1WorkoutsWorkoutid(workoutId, headers, { client }),
1595
+ createWorkout: (data) => postV1Workouts(headers, data, { client }),
1596
+ updateWorkout: (workoutId, data) => putV1WorkoutsWorkoutid(workoutId, headers, data, {
1597
+ client
1598
+ }),
1599
+ getWorkoutCount: () => getV1WorkoutsCount(headers, { client }),
1600
+ getWorkoutEvents: (params) => getV1WorkoutsEvents(headers, params, { client }),
1601
+ // Routines
1602
+ getRoutines: (params) => getV1Routines(headers, params, { client }),
1603
+ getRoutineById: (routineId) => getV1RoutinesRoutineid(routineId, headers, { client }),
1604
+ createRoutine: (data) => postV1Routines(headers, data, { client }),
1605
+ updateRoutine: (routineId, data) => putV1RoutinesRoutineid(routineId, headers, data, {
1606
+ client
1607
+ }),
1608
+ // Exercise Templates
1609
+ getExerciseTemplates: (params) => getV1ExerciseTemplates(headers, params, { client }),
1610
+ getExerciseTemplate: (templateId) => getV1ExerciseTemplatesExercisetemplateid(templateId, headers, {
1611
+ client
1612
+ }),
1613
+ // Routine Folders
1614
+ getRoutineFolders: (params) => getV1RoutineFolders(headers, params, { client }),
1615
+ createRoutineFolder: (data) => postV1RoutineFolders(headers, data, { client }),
1616
+ getRoutineFolder: (folderId) => getV1RoutineFoldersFolderid(folderId, headers, {
1617
+ client
1618
+ }),
1619
+ // Webhooks
1620
+ getWebhookSubscription: () => getV1WebhookSubscription(headers, { client }),
1621
+ createWebhookSubscription: (data) => postV1WebhookSubscription(headers, data, { client }),
1622
+ deleteWebhookSubscription: () => deleteV1WebhookSubscription(headers, { client })
1623
+ };
1624
+ }
1625
+
1626
+ // src/utils/hevyClient.ts
1627
+ function createClient2(apiKey, baseUrl) {
1628
+ return createClient(apiKey, baseUrl);
1629
+ }
1630
+
1631
+ // src/index.ts
1632
+ dotenvx.config({ quiet: true });
1633
+ 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).")
1636
+ });
1637
+ function buildServer(apiKey) {
1638
+ const server = new McpServer({
1639
+ name,
1640
+ version
1641
+ });
1642
+ const hevyClient = createClient2(apiKey, HEVY_API_BASEURL);
1643
+ console.error("Hevy client initialized with API key");
1644
+ registerWorkoutTools(server, hevyClient);
1645
+ registerRoutineTools(server, hevyClient);
1646
+ registerTemplateTools(server, hevyClient);
1647
+ registerFolderTools(server, hevyClient);
1648
+ registerWebhookTools(server, hevyClient);
1649
+ return server;
1650
+ }
1651
+ async function runServer() {
1652
+ const args = process.argv.slice(2);
1653
+ const cfg = parseConfig(args, process.env);
1654
+ const apiKey = cfg.apiKey;
1655
+ assertApiKey(apiKey);
1656
+ const server = buildServer(apiKey);
1657
+ console.error("Starting MCP server in stdio mode");
1658
+ const transport = new StdioServerTransport();
1659
+ await server.connect(transport);
1660
+ }
1661
+
1662
+ // src/cli.ts
1663
+ void runServer().catch((error) => {
1664
+ console.error("Fatal error in main():", error);
1665
+ process.exit(1);
1666
+ });
1667
+ //# sourceMappingURL=cli.js.map