hevy-mcp 1.11.10 → 1.12.4

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/index.js CHANGED
@@ -4,12 +4,14 @@
4
4
 
5
5
  // src/index.ts
6
6
  import "@dotenvx/dotenvx/config";
7
+ import { fileURLToPath } from "url";
7
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
+ import { z as z42 } from "zod";
9
11
 
10
12
  // package.json
11
13
  var name = "hevy-mcp";
12
- var version = "1.11.9";
14
+ var version = "1.12.3";
13
15
 
14
16
  // src/tools/folders.ts
15
17
  import { z } from "zod";
@@ -53,9 +55,9 @@ function determineErrorType(error, message) {
53
55
  return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
54
56
  }
55
57
  function withErrorHandling(fn, context) {
56
- return (async (...args2) => {
58
+ return (async (...args) => {
57
59
  try {
58
- return await fn(...args2);
60
+ return await fn(...args);
59
61
  } catch (error) {
60
62
  return createErrorResponse(error, context);
61
63
  }
@@ -188,8 +190,8 @@ function createEmptyResponse(message = "No data found") {
188
190
  }
189
191
 
190
192
  // src/tools/folders.ts
191
- function registerFolderTools(server2, hevyClient2) {
192
- server2.tool(
193
+ function registerFolderTools(server, hevyClient) {
194
+ server.tool(
193
195
  "get-routine-folders",
194
196
  "Get a paginated list of your routine folders, including both default and custom folders. Useful for organizing and browsing your workout routines.",
195
197
  {
@@ -198,12 +200,12 @@ function registerFolderTools(server2, hevyClient2) {
198
200
  },
199
201
  withErrorHandling(
200
202
  async ({ page, pageSize }) => {
201
- if (!hevyClient2) {
203
+ if (!hevyClient) {
202
204
  throw new Error(
203
205
  "API client not initialized. Please provide HEVY_API_KEY."
204
206
  );
205
207
  }
206
- const data = await hevyClient2.getRoutineFolders({
208
+ const data = await hevyClient.getRoutineFolders({
207
209
  page,
208
210
  pageSize
209
211
  });
@@ -220,19 +222,19 @@ function registerFolderTools(server2, hevyClient2) {
220
222
  "get-routine-folders"
221
223
  )
222
224
  );
223
- server2.tool(
225
+ server.tool(
224
226
  "get-routine-folder",
225
227
  "Get complete details of a specific routine folder by its ID, including name, creation date, and associated routines.",
226
228
  {
227
229
  folderId: z.string().min(1)
228
230
  },
229
231
  withErrorHandling(async ({ folderId }) => {
230
- if (!hevyClient2) {
232
+ if (!hevyClient) {
231
233
  throw new Error(
232
234
  "API client not initialized. Please provide HEVY_API_KEY."
233
235
  );
234
236
  }
235
- const data = await hevyClient2.getRoutineFolder(folderId);
237
+ const data = await hevyClient.getRoutineFolder(folderId);
236
238
  if (!data) {
237
239
  return createEmptyResponse(
238
240
  `Routine folder with ID ${folderId} not found`
@@ -242,19 +244,19 @@ function registerFolderTools(server2, hevyClient2) {
242
244
  return createJsonResponse(folder);
243
245
  }, "get-routine-folder")
244
246
  );
245
- server2.tool(
247
+ server.tool(
246
248
  "create-routine-folder",
247
249
  "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.",
248
250
  {
249
251
  name: z.string().min(1)
250
252
  },
251
253
  withErrorHandling(async ({ name: name2 }) => {
252
- if (!hevyClient2) {
254
+ if (!hevyClient) {
253
255
  throw new Error(
254
256
  "API client not initialized. Please provide HEVY_API_KEY."
255
257
  );
256
258
  }
257
- const data = await hevyClient2.createRoutineFolder({
259
+ const data = await hevyClient.createRoutineFolder({
258
260
  routine_folder: {
259
261
  title: name2
260
262
  }
@@ -275,22 +277,22 @@ function registerFolderTools(server2, hevyClient2) {
275
277
 
276
278
  // src/tools/routines.ts
277
279
  import { z as z2 } from "zod";
278
- function registerRoutineTools(server2, hevyClient2) {
279
- server2.tool(
280
+ function registerRoutineTools(server, hevyClient) {
281
+ server.tool(
280
282
  "get-routines",
281
283
  "Get a paginated list of your workout routines, including custom and default routines. Useful for browsing or searching your available routines.",
282
284
  {
283
285
  page: z2.coerce.number().int().gte(1).default(1),
284
286
  pageSize: z2.coerce.number().int().gte(1).lte(10).default(5)
285
287
  },
286
- withErrorHandling(async (args2) => {
287
- if (!hevyClient2) {
288
+ withErrorHandling(async (args) => {
289
+ if (!hevyClient) {
288
290
  throw new Error(
289
291
  "API client not initialized. Please provide HEVY_API_KEY."
290
292
  );
291
293
  }
292
- const { page, pageSize } = args2;
293
- const data = await hevyClient2.getRoutines({
294
+ const { page, pageSize } = args;
295
+ const data = await hevyClient.getRoutines({
294
296
  page,
295
297
  pageSize
296
298
  });
@@ -303,19 +305,19 @@ function registerRoutineTools(server2, hevyClient2) {
303
305
  return createJsonResponse(routines);
304
306
  }, "get-routines")
305
307
  );
306
- server2.tool(
308
+ server.tool(
307
309
  "get-routine",
308
310
  "Get a routine by its ID using the direct endpoint. Returns all details for the specified routine.",
309
311
  {
310
312
  routineId: z2.string().min(1)
311
313
  },
312
314
  withErrorHandling(async ({ routineId }) => {
313
- if (!hevyClient2) {
315
+ if (!hevyClient) {
314
316
  throw new Error(
315
317
  "API client not initialized. Please provide HEVY_API_KEY."
316
318
  );
317
319
  }
318
- const data = await hevyClient2.getRoutineById(String(routineId));
320
+ const data = await hevyClient.getRoutineById(String(routineId));
319
321
  if (!data || !data.routine) {
320
322
  return createEmptyResponse(`Routine with ID ${routineId} not found`);
321
323
  }
@@ -323,7 +325,7 @@ function registerRoutineTools(server2, hevyClient2) {
323
325
  return createJsonResponse(routine);
324
326
  }, "get-routine")
325
327
  );
326
- server2.tool(
328
+ server.tool(
327
329
  "create-routine",
328
330
  "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.",
329
331
  {
@@ -349,14 +351,14 @@ function registerRoutineTools(server2, hevyClient2) {
349
351
  })
350
352
  )
351
353
  },
352
- withErrorHandling(async (args2) => {
353
- if (!hevyClient2) {
354
+ withErrorHandling(async (args) => {
355
+ if (!hevyClient) {
354
356
  throw new Error(
355
357
  "API client not initialized. Please provide HEVY_API_KEY."
356
358
  );
357
359
  }
358
- const { title, folderId, notes, exercises } = args2;
359
- const data = await hevyClient2.createRoutine({
360
+ const { title, folderId, notes, exercises } = args;
361
+ const data = await hevyClient.createRoutine({
360
362
  routine: {
361
363
  title,
362
364
  folder_id: folderId ?? null,
@@ -393,7 +395,7 @@ function registerRoutineTools(server2, hevyClient2) {
393
395
  });
394
396
  }, "create-routine")
395
397
  );
396
- server2.tool(
398
+ server.tool(
397
399
  "update-routine",
398
400
  "Update an existing routine by ID. You can modify the title, notes, and exercise configurations. Returns the updated routine with all changes applied.",
399
401
  {
@@ -419,14 +421,14 @@ function registerRoutineTools(server2, hevyClient2) {
419
421
  })
420
422
  )
421
423
  },
422
- withErrorHandling(async (args2) => {
423
- if (!hevyClient2) {
424
+ withErrorHandling(async (args) => {
425
+ if (!hevyClient) {
424
426
  throw new Error(
425
427
  "API client not initialized. Please provide HEVY_API_KEY."
426
428
  );
427
429
  }
428
- const { routineId, title, notes, exercises } = args2;
429
- const data = await hevyClient2.updateRoutine(routineId, {
430
+ const { routineId, title, notes, exercises } = args;
431
+ const data = await hevyClient.updateRoutine(routineId, {
430
432
  routine: {
431
433
  title,
432
434
  notes: notes ?? null,
@@ -466,8 +468,8 @@ function registerRoutineTools(server2, hevyClient2) {
466
468
 
467
469
  // src/tools/templates.ts
468
470
  import { z as z3 } from "zod";
469
- function registerTemplateTools(server2, hevyClient2) {
470
- server2.tool(
471
+ function registerTemplateTools(server, hevyClient) {
472
+ server.tool(
471
473
  "get-exercise-templates",
472
474
  "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.",
473
475
  {
@@ -476,12 +478,12 @@ function registerTemplateTools(server2, hevyClient2) {
476
478
  },
477
479
  withErrorHandling(
478
480
  async ({ page, pageSize }) => {
479
- if (!hevyClient2) {
481
+ if (!hevyClient) {
480
482
  throw new Error(
481
483
  "API client not initialized. Please provide HEVY_API_KEY."
482
484
  );
483
485
  }
484
- const data = await hevyClient2.getExerciseTemplates({
486
+ const data = await hevyClient.getExerciseTemplates({
485
487
  page,
486
488
  pageSize
487
489
  });
@@ -498,7 +500,7 @@ function registerTemplateTools(server2, hevyClient2) {
498
500
  "get-exercise-templates"
499
501
  )
500
502
  );
501
- server2.tool(
503
+ server.tool(
502
504
  "get-exercise-template",
503
505
  "Get complete details of a specific exercise template by its ID, including name, category, equipment, muscle groups, and notes.",
504
506
  {
@@ -506,12 +508,12 @@ function registerTemplateTools(server2, hevyClient2) {
506
508
  },
507
509
  withErrorHandling(
508
510
  async ({ exerciseTemplateId }) => {
509
- if (!hevyClient2) {
511
+ if (!hevyClient) {
510
512
  throw new Error(
511
513
  "API client not initialized. Please provide HEVY_API_KEY."
512
514
  );
513
515
  }
514
- const data = await hevyClient2.getExerciseTemplate(exerciseTemplateId);
516
+ const data = await hevyClient.getExerciseTemplate(exerciseTemplateId);
515
517
  if (!data) {
516
518
  return createEmptyResponse(
517
519
  `Exercise template with ID ${exerciseTemplateId} not found`
@@ -994,18 +996,18 @@ var webhookUrlSchema = z40.string().url().refine(
994
996
  message: "Webhook URL cannot be localhost or loopback address"
995
997
  }
996
998
  );
997
- function registerWebhookTools(server2, hevyClient2) {
998
- server2.tool(
999
+ function registerWebhookTools(server, hevyClient) {
1000
+ server.tool(
999
1001
  "get-webhook-subscription",
1000
1002
  "Get the current webhook subscription for this account. Returns the webhook URL and auth token if a subscription exists.",
1001
1003
  {},
1002
1004
  withErrorHandling(async () => {
1003
- if (!hevyClient2) {
1005
+ if (!hevyClient) {
1004
1006
  throw new Error(
1005
1007
  "API client not initialized. Please provide HEVY_API_KEY."
1006
1008
  );
1007
1009
  }
1008
- const data = await hevyClient2.getWebhookSubscription();
1010
+ const data = await hevyClient.getWebhookSubscription();
1009
1011
  if (!data) {
1010
1012
  return createEmptyResponse(
1011
1013
  "No webhook subscription found for this account"
@@ -1014,7 +1016,7 @@ function registerWebhookTools(server2, hevyClient2) {
1014
1016
  return createJsonResponse(data);
1015
1017
  }, "get-webhook-subscription")
1016
1018
  );
1017
- server2.tool(
1019
+ server.tool(
1018
1020
  "create-webhook-subscription",
1019
1021
  "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.",
1020
1022
  {
@@ -1026,7 +1028,7 @@ function registerWebhookTools(server2, hevyClient2) {
1026
1028
  )
1027
1029
  },
1028
1030
  withErrorHandling(async ({ url, authToken }) => {
1029
- if (!hevyClient2) {
1031
+ if (!hevyClient) {
1030
1032
  throw new Error(
1031
1033
  "API client not initialized. Please provide HEVY_API_KEY."
1032
1034
  );
@@ -1035,7 +1037,7 @@ function registerWebhookTools(server2, hevyClient2) {
1035
1037
  url,
1036
1038
  authToken
1037
1039
  });
1038
- const data = await hevyClient2.createWebhookSubscription(requestBody);
1040
+ const data = await hevyClient.createWebhookSubscription(requestBody);
1039
1041
  if (!data) {
1040
1042
  return createEmptyResponse(
1041
1043
  "Failed to create webhook subscription - please check your URL and try again"
@@ -1044,17 +1046,17 @@ function registerWebhookTools(server2, hevyClient2) {
1044
1046
  return createJsonResponse(data);
1045
1047
  }, "create-webhook-subscription")
1046
1048
  );
1047
- server2.tool(
1049
+ server.tool(
1048
1050
  "delete-webhook-subscription",
1049
1051
  "Delete the current webhook subscription for this account. This will stop all webhook notifications.",
1050
1052
  {},
1051
1053
  withErrorHandling(async () => {
1052
- if (!hevyClient2) {
1054
+ if (!hevyClient) {
1053
1055
  throw new Error(
1054
1056
  "API client not initialized. Please provide HEVY_API_KEY."
1055
1057
  );
1056
1058
  }
1057
- const data = await hevyClient2.deleteWebhookSubscription();
1059
+ const data = await hevyClient.deleteWebhookSubscription();
1058
1060
  if (!data) {
1059
1061
  return createEmptyResponse(
1060
1062
  "Failed to delete webhook subscription - no subscription may exist or there was a server error"
@@ -1067,8 +1069,8 @@ function registerWebhookTools(server2, hevyClient2) {
1067
1069
 
1068
1070
  // src/tools/workouts.ts
1069
1071
  import { z as z41 } from "zod";
1070
- function registerWorkoutTools(server2, hevyClient2) {
1071
- server2.tool(
1072
+ function registerWorkoutTools(server, hevyClient) {
1073
+ server.tool(
1072
1074
  "get-workouts",
1073
1075
  "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.",
1074
1076
  {
@@ -1076,12 +1078,12 @@ function registerWorkoutTools(server2, hevyClient2) {
1076
1078
  pageSize: z41.coerce.number().int().gte(1).lte(10).default(5)
1077
1079
  },
1078
1080
  withErrorHandling(async ({ page, pageSize }) => {
1079
- if (!hevyClient2) {
1081
+ if (!hevyClient) {
1080
1082
  throw new Error(
1081
1083
  "API client not initialized. Please provide HEVY_API_KEY."
1082
1084
  );
1083
1085
  }
1084
- const data = await hevyClient2.getWorkouts({
1086
+ const data = await hevyClient.getWorkouts({
1085
1087
  page,
1086
1088
  pageSize
1087
1089
  });
@@ -1094,19 +1096,19 @@ function registerWorkoutTools(server2, hevyClient2) {
1094
1096
  return createJsonResponse(workouts);
1095
1097
  }, "get-workouts")
1096
1098
  );
1097
- server2.tool(
1099
+ server.tool(
1098
1100
  "get-workout",
1099
1101
  "Get complete details of a specific workout by ID. Returns all workout information including title, description, start/end times, and detailed exercise data.",
1100
1102
  {
1101
1103
  workoutId: z41.string().min(1)
1102
1104
  },
1103
1105
  withErrorHandling(async ({ workoutId }) => {
1104
- if (!hevyClient2) {
1106
+ if (!hevyClient) {
1105
1107
  throw new Error(
1106
1108
  "API client not initialized. Please provide HEVY_API_KEY."
1107
1109
  );
1108
1110
  }
1109
- const data = await hevyClient2.getWorkout(workoutId);
1111
+ const data = await hevyClient.getWorkout(workoutId);
1110
1112
  if (!data) {
1111
1113
  return createEmptyResponse(`Workout with ID ${workoutId} not found`);
1112
1114
  }
@@ -1114,22 +1116,22 @@ function registerWorkoutTools(server2, hevyClient2) {
1114
1116
  return createJsonResponse(workout);
1115
1117
  }, "get-workout")
1116
1118
  );
1117
- server2.tool(
1119
+ server.tool(
1118
1120
  "get-workout-count",
1119
1121
  "Get the total number of workouts on the account. Useful for pagination or statistics.",
1120
1122
  {},
1121
1123
  withErrorHandling(async () => {
1122
- if (!hevyClient2) {
1124
+ if (!hevyClient) {
1123
1125
  throw new Error(
1124
1126
  "API client not initialized. Please provide HEVY_API_KEY."
1125
1127
  );
1126
1128
  }
1127
- const data = await hevyClient2.getWorkoutCount();
1129
+ const data = await hevyClient.getWorkoutCount();
1128
1130
  const count = data ? data.workoutCount || 0 : 0;
1129
1131
  return createJsonResponse({ count });
1130
1132
  }, "get-workout-count")
1131
1133
  );
1132
- server2.tool(
1134
+ server.tool(
1133
1135
  "get-workout-events",
1134
1136
  "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.",
1135
1137
  {
@@ -1138,12 +1140,12 @@ function registerWorkoutTools(server2, hevyClient2) {
1138
1140
  since: z41.string().default("1970-01-01T00:00:00Z")
1139
1141
  },
1140
1142
  withErrorHandling(async ({ page, pageSize, since }) => {
1141
- if (!hevyClient2) {
1143
+ if (!hevyClient) {
1142
1144
  throw new Error(
1143
1145
  "API client not initialized. Please provide HEVY_API_KEY."
1144
1146
  );
1145
1147
  }
1146
- const data = await hevyClient2.getWorkoutEvents({
1148
+ const data = await hevyClient.getWorkoutEvents({
1147
1149
  page,
1148
1150
  pageSize,
1149
1151
  since
@@ -1157,7 +1159,7 @@ function registerWorkoutTools(server2, hevyClient2) {
1157
1159
  return createJsonResponse(events);
1158
1160
  }, "get-workout-events")
1159
1161
  );
1160
- server2.tool(
1162
+ server.tool(
1161
1163
  "create-workout",
1162
1164
  "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.",
1163
1165
  {
@@ -1194,7 +1196,7 @@ function registerWorkoutTools(server2, hevyClient2) {
1194
1196
  isPrivate,
1195
1197
  exercises
1196
1198
  }) => {
1197
- if (!hevyClient2) {
1199
+ if (!hevyClient) {
1198
1200
  throw new Error(
1199
1201
  "API client not initialized. Please provide HEVY_API_KEY."
1200
1202
  );
@@ -1222,7 +1224,7 @@ function registerWorkoutTools(server2, hevyClient2) {
1222
1224
  }))
1223
1225
  }
1224
1226
  };
1225
- const data = await hevyClient2.createWorkout(requestBody);
1227
+ const data = await hevyClient.createWorkout(requestBody);
1226
1228
  if (!data) {
1227
1229
  return createEmptyResponse(
1228
1230
  "Failed to create workout: Server returned no data"
@@ -1237,7 +1239,7 @@ function registerWorkoutTools(server2, hevyClient2) {
1237
1239
  "create-workout"
1238
1240
  )
1239
1241
  );
1240
- server2.tool(
1242
+ server.tool(
1241
1243
  "update-workout",
1242
1244
  "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.",
1243
1245
  {
@@ -1299,7 +1301,7 @@ function registerWorkoutTools(server2, hevyClient2) {
1299
1301
  }))
1300
1302
  }
1301
1303
  };
1302
- const data = await hevyClient2.updateWorkout(workoutId, requestBody);
1304
+ const data = await hevyClient.updateWorkout(workoutId, requestBody);
1303
1305
  if (!data) {
1304
1306
  return createEmptyResponse(
1305
1307
  `Failed to update workout with ID ${workoutId}`
@@ -1337,24 +1339,14 @@ function parseConfig(argv, env) {
1337
1339
  if (!apiKey) {
1338
1340
  apiKey = env.HEVY_API_KEY || "";
1339
1341
  }
1340
- const transportMode = argv.includes("--http") || env.MCP_TRANSPORT === "http" ? "http" : "stdio";
1341
- const httpPort = Number.parseInt(env.MCP_HTTP_PORT || "3000", 10);
1342
- const httpHost = env.MCP_HTTP_HOST || "127.0.0.1";
1343
- const enableDnsRebindingProtection = env.MCP_DNS_REBINDING_PROTECTION === "true";
1344
- const allowedHosts = env.MCP_ALLOWED_HOSTS?.split(",").map((h) => h.trim()).filter(Boolean) || ["127.0.0.1"];
1345
1342
  return {
1346
- apiKey,
1347
- transportMode,
1348
- httpHost,
1349
- httpPort,
1350
- enableDnsRebindingProtection,
1351
- allowedHosts
1343
+ apiKey
1352
1344
  };
1353
1345
  }
1354
1346
  function assertApiKey(apiKey) {
1355
1347
  if (!apiKey) {
1356
1348
  console.error(
1357
- "Hevy API key is required. Provide via HEVY_API_KEY env variable, --hevy-api-key=YOUR_KEY command argument, or ?HEVY_API_KEY=YOUR_KEY query parameter."
1349
+ "Hevy API key is required. Provide it via the HEVY_API_KEY environment variable or the --hevy-api-key=YOUR_KEY command argument."
1358
1350
  );
1359
1351
  process.exit(1);
1360
1352
  }
@@ -1637,202 +1629,63 @@ function createClient2(apiKey, baseUrl) {
1637
1629
  return createClient(apiKey, baseUrl);
1638
1630
  }
1639
1631
 
1640
- // src/utils/httpServer.ts
1641
- import { randomUUID } from "crypto";
1642
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
1643
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
1644
- import express from "express";
1645
- var transports = /* @__PURE__ */ new Map();
1646
- var SESSION_TIMEOUT = 30 * 60 * 1e3;
1647
- setInterval(
1648
- () => {
1649
- const now = Date.now();
1650
- for (const [sessionId, session] of transports) {
1651
- if (now - session.lastActivity > SESSION_TIMEOUT) {
1652
- console.log(`Cleaning up abandoned session: ${sessionId}`);
1653
- session.transport.close?.();
1654
- transports.delete(sessionId);
1655
- }
1656
- }
1657
- },
1658
- 5 * 60 * 1e3
1659
- );
1660
- function createHttpServer(server2, options) {
1661
- const app = express();
1662
- const port = options?.port || 3e3;
1663
- const host = options?.host || "127.0.0.1";
1664
- app.use(express.json());
1665
- app.use("/mcp", (req, res, next) => {
1666
- res.setHeader("Access-Control-Allow-Origin", "*");
1667
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
1668
- res.setHeader(
1669
- "Access-Control-Allow-Headers",
1670
- "Content-Type, mcp-session-id"
1671
- );
1672
- res.setHeader(
1673
- "Access-Control-Expose-Headers",
1674
- "mcp-session-id, mcp-protocol-version"
1675
- );
1676
- if (req.method === "OPTIONS") {
1677
- res.status(204).end();
1678
- return;
1679
- }
1680
- next();
1681
- });
1682
- app.post("/mcp", async (req, res) => {
1683
- const sessionId = req.headers["mcp-session-id"];
1684
- let transport;
1685
- if (sessionId && transports.has(sessionId)) {
1686
- const existingSession = transports.get(sessionId);
1687
- if (!existingSession) {
1688
- res.status(400).json({
1689
- jsonrpc: "2.0",
1690
- error: {
1691
- code: -32e3,
1692
- message: "Bad Request: Session lookup failed"
1693
- },
1694
- id: null
1695
- });
1696
- return;
1697
- }
1698
- transport = existingSession.transport;
1699
- existingSession.lastActivity = Date.now();
1700
- } else if (!sessionId && isInitializeRequest(req.body)) {
1701
- const queryApiKey = req.query.HEVY_API_KEY;
1702
- if (queryApiKey && options?.onFirstRequestApiKey) {
1703
- options.onFirstRequestApiKey(queryApiKey);
1704
- }
1705
- transport = new StreamableHTTPServerTransport({
1706
- sessionIdGenerator: () => randomUUID(),
1707
- onsessioninitialized: (sessionId2) => {
1708
- transports.set(sessionId2, { transport, lastActivity: Date.now() });
1709
- },
1710
- // DNS rebinding protection configuration
1711
- enableDnsRebindingProtection: options?.enableDnsRebindingProtection ?? false,
1712
- allowedHosts: options?.allowedHosts || ["127.0.0.1"]
1713
- });
1714
- transport.onclose = () => {
1715
- if (transport.sessionId) {
1716
- transports.delete(transport.sessionId);
1717
- }
1718
- };
1719
- await server2.connect(transport);
1720
- } else {
1721
- res.status(400).json({
1722
- jsonrpc: "2.0",
1723
- error: {
1724
- code: -32e3,
1725
- message: "Bad Request: No valid session ID provided"
1726
- },
1727
- id: null
1728
- });
1729
- return;
1730
- }
1731
- await transport.handleRequest(req, res, req.body);
1732
- });
1733
- const handleSessionRequest = async (req, res) => {
1734
- const sessionId = req.headers["mcp-session-id"];
1735
- if (!sessionId || !transports.has(sessionId)) {
1736
- res.status(400).send("Invalid or missing session ID");
1737
- return;
1738
- }
1739
- const transport = transports.get(sessionId)?.transport;
1740
- if (transport) {
1741
- await transport.handleRequest(req, res);
1742
- }
1743
- };
1744
- app.get("/mcp", handleSessionRequest);
1745
- app.delete("/mcp", handleSessionRequest);
1746
- app.get("/health", (_req, res) => {
1747
- res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1748
- });
1749
- const startServer = () => {
1750
- return new Promise((resolve, reject) => {
1751
- const httpServer = app.listen(port, host, () => {
1752
- console.log(`MCP HTTP server listening on http://${host}:${port}`);
1753
- console.log(`MCP endpoint: http://${host}:${port}/mcp`);
1754
- console.log(`Health check: http://${host}:${port}/health`);
1755
- resolve();
1756
- });
1757
- httpServer.on("error", (error) => {
1758
- reject(error);
1759
- });
1760
- });
1761
- };
1762
- return {
1763
- app,
1764
- startServer,
1765
- getActiveSessionsCount: () => transports.size,
1766
- closeAllSessions: () => {
1767
- for (const session of transports.values()) {
1768
- session.transport.close?.();
1769
- }
1770
- transports.clear();
1771
- }
1772
- };
1773
- }
1774
-
1775
1632
  // src/index.ts
1776
1633
  var HEVY_API_BASEURL = "https://api.hevyapp.com";
1777
- var args = process.argv.slice(2);
1778
- var cfg = parseConfig(args, process.env);
1779
- var server = new McpServer({
1780
- name,
1781
- version
1782
- });
1783
- var hevyClient = null;
1784
- function initializeClient(apiKey) {
1785
- if (!hevyClient) {
1786
- assertApiKey(apiKey);
1787
- hevyClient = createClient2(apiKey, HEVY_API_BASEURL);
1788
- console.log("Hevy client initialized with API key");
1789
- }
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
+ var configSchema = serverConfigSchema;
1638
+ function buildServer(apiKey) {
1639
+ const server = new McpServer({
1640
+ name,
1641
+ version
1642
+ });
1643
+ const hevyClient = createClient2(apiKey, HEVY_API_BASEURL);
1644
+ console.log("Hevy client initialized with API key");
1645
+ registerWorkoutTools(server, hevyClient);
1646
+ registerRoutineTools(server, hevyClient);
1647
+ registerTemplateTools(server, hevyClient);
1648
+ registerFolderTools(server, hevyClient);
1649
+ registerWebhookTools(server, hevyClient);
1650
+ return server;
1790
1651
  }
1791
- if (cfg.transportMode === "stdio") {
1792
- initializeClient(cfg.apiKey);
1793
- } else if (cfg.apiKey) {
1794
- initializeClient(cfg.apiKey);
1795
- } else {
1796
- console.log(
1797
- "Starting in HTTP mode without API key. Waiting for API key via query parameter on first request."
1798
- );
1652
+ function createServer({ config }) {
1653
+ const { apiKey } = serverConfigSchema.parse(config);
1654
+ const server = buildServer(apiKey);
1655
+ return server.server;
1799
1656
  }
1800
- registerWorkoutTools(server, hevyClient);
1801
- registerRoutineTools(server, hevyClient);
1802
- registerTemplateTools(server, hevyClient);
1803
- registerFolderTools(server, hevyClient);
1804
- registerWebhookTools(server, hevyClient);
1805
1657
  async function runServer() {
1806
- if (cfg.transportMode === "http") {
1807
- console.log(
1808
- `Starting MCP server in HTTP mode on ${cfg.httpHost}:${cfg.httpPort}`
1809
- );
1810
- const httpServer = createHttpServer(server, {
1811
- port: cfg.httpPort,
1812
- host: cfg.httpHost,
1813
- enableDnsRebindingProtection: cfg.enableDnsRebindingProtection,
1814
- allowedHosts: cfg.allowedHosts,
1815
- // Callback to handle API key from query params
1816
- onFirstRequestApiKey: (apiKey) => {
1817
- if (!hevyClient) {
1818
- initializeClient(apiKey);
1819
- registerWorkoutTools(server, hevyClient);
1820
- registerRoutineTools(server, hevyClient);
1821
- registerTemplateTools(server, hevyClient);
1822
- registerFolderTools(server, hevyClient);
1823
- registerWebhookTools(server, hevyClient);
1824
- }
1825
- }
1826
- });
1827
- await httpServer.startServer();
1828
- } else {
1829
- console.log("Starting MCP server in stdio mode");
1830
- const transport = new StdioServerTransport();
1831
- await server.connect(transport);
1658
+ const args = process.argv.slice(2);
1659
+ const cfg = parseConfig(args, process.env);
1660
+ const apiKey = cfg.apiKey;
1661
+ assertApiKey(apiKey);
1662
+ const server = buildServer(apiKey);
1663
+ console.log("Starting MCP server in stdio mode");
1664
+ const transport = new StdioServerTransport();
1665
+ await server.connect(transport);
1666
+ }
1667
+ var isDirectExecution = (() => {
1668
+ if (typeof process === "undefined" || !Array.isArray(process.argv)) {
1669
+ return false;
1832
1670
  }
1671
+ if (typeof import.meta === "undefined" || !import.meta?.url) {
1672
+ return false;
1673
+ }
1674
+ try {
1675
+ const modulePath = fileURLToPath(import.meta.url);
1676
+ return process.argv[1] === modulePath;
1677
+ } catch {
1678
+ return false;
1679
+ }
1680
+ })();
1681
+ if (isDirectExecution) {
1682
+ runServer().catch((error) => {
1683
+ console.error("Fatal error in main():", error);
1684
+ process.exit(1);
1685
+ });
1833
1686
  }
1834
- runServer().catch((error) => {
1835
- console.error("Fatal error in main():", error);
1836
- process.exit(1);
1837
- });
1687
+ export {
1688
+ configSchema,
1689
+ createServer as default
1690
+ };
1838
1691
  //# sourceMappingURL=index.js.map