hevy-mcp 4.1.1 → 4.1.3

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.mjs CHANGED
@@ -4,12 +4,12 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.1" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.3" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "625c4c9c-e20d-40b3-96b1-4e799ef130f3", e._sentryDebugIdIdentifier = "sentry-dbid-625c4c9c-e20d-40b3-96b1-4e799ef130f3");
10
10
  } catch (e) {}
11
11
  })();
12
- import { a as createSafeErrorDiagnostic, i as MissingHevyApiKeyError, n as runServer, o as flushTelemetry } from "./src-BPbLELQf.mjs";
12
+ import { a as createSafeErrorDiagnostic, i as MissingHevyApiKeyError, n as runServer, o as flushTelemetry } from "./src-CBiQ6f-q.mjs";
13
13
  //#region src/cli.ts
14
14
  runServer().catch(async (error) => {
15
15
  if (error instanceof MissingHevyApiKeyError) console.error(error.message);
package/dist/index.d.mts CHANGED
@@ -5,7 +5,7 @@ type NodeTransport = "stdio" | "http";
5
5
  //#region src/index.d.ts
6
6
  declare function createNodeMcpServer({ apiKey }: {
7
7
  apiKey: string;
8
- }, transport?: NodeTransport): Promise<import("@modelcontextprotocol/sdk/server/mcp").McpServer>;
8
+ }, transport?: NodeTransport): Promise<import("@modelcontextprotocol/server").McpServer>;
9
9
  declare function runStdioServer(): Promise<void>;
10
10
  declare function runServer(): Promise<void>;
11
11
  //#endregion
package/dist/index.mjs CHANGED
@@ -4,10 +4,10 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.1" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.3" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "e9496b31-626b-4ad1-9416-47ba4ffd2995", e._sentryDebugIdIdentifier = "sentry-dbid-e9496b31-626b-4ad1-9416-47ba4ffd2995");
10
10
  } catch (e) {}
11
11
  })();
12
- import { n as runServer, r as runStdioServer, t as createNodeMcpServer } from "./src-BPbLELQf.mjs";
12
+ import { n as runServer, r as runStdioServer, t as createNodeMcpServer } from "./src-CBiQ6f-q.mjs";
13
13
  export { createNodeMcpServer, runServer, runStdioServer };
@@ -4,9 +4,9 @@
4
4
  (function() {
5
5
  try {
6
6
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
7
- e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.1" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@4.1.3" };
8
8
  var n = new e.Error().stack;
9
- n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "3ad9084e-6d6c-4681-ad27-9168114359ab", e._sentryDebugIdIdentifier = "sentry-dbid-3ad9084e-6d6c-4681-ad27-9168114359ab");
9
+ n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "112c763b-1652-4fb5-8237-94b6a8ef1122", e._sentryDebugIdIdentifier = "sentry-dbid-112c763b-1652-4fb5-8237-94b6a8ef1122");
10
10
  } catch (e) {}
11
11
  })();
12
12
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
@@ -19,15 +19,14 @@ import { resourceFromAttributes } from "@opentelemetry/resources";
19
19
  import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
20
20
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
21
21
  import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
22
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
23
23
  import { z } from "zod";
24
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
24
+ import { McpServer, deserializeMessage } from "@modelcontextprotocol/server";
25
25
  import { z as z$1 } from "zod/v4";
26
26
  import { isIP } from "node:net";
27
27
  import { createServer } from "node:http";
28
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
28
+ import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
29
29
  import { AsyncLocalStorage } from "node:async_hooks";
30
- import { deserializeMessage } from "@modelcontextprotocol/sdk/shared/stdio.js";
31
30
  import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
32
31
  import { homedir } from "node:os";
33
32
  import { dirname, join } from "node:path";
@@ -81,11 +80,11 @@ function sanitizeSentryMcpSpan(span) {
81
80
  * OTel Collector → Honeycomb: performance traces, metrics
82
81
  */
83
82
  const name$1 = "hevy-mcp";
84
- const version$1 = "4.1.1";
83
+ const version$1 = "4.1.3";
85
84
  const telemetryEnabled = process.env.HEVY_MCP_TELEMETRY !== "0";
86
85
  const collectorToken = "NH9vOela-HYreQxAbJa68cjEmORoEKM57EvneUDVcOo";
87
86
  const COLLECTOR_ENDPOINT = "https://otel.chrisdoc.dev/v1";
88
- const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@4.1.1`;
87
+ const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@4.1.3`;
89
88
  const resource = resourceFromAttributes({
90
89
  "service.name": name$1,
91
90
  "service.version": version$1
@@ -220,10 +219,15 @@ function coerceNullishNumberInput(value) {
220
219
  }
221
220
  const zNullableInt = z.preprocess(coerceNullishNumberInput, z.number().int().nullable().optional());
222
221
  const zNullableNumber = z.preprocess((value) => value === "" ? void 0 : value, z.coerce.number().nullable().optional());
223
- const zOptionalRepRange = z.preprocess((value) => value === null ? void 0 : value, z.object({
222
+ const repRangeShape = {
224
223
  start: zNullableInt,
225
224
  end: zNullableInt
226
- }).optional());
225
+ };
226
+ function optionalRepRangeSchema(schema) {
227
+ return z.preprocess((value) => value === null ? void 0 : value, schema);
228
+ }
229
+ const zOptionalRepRange = optionalRepRangeSchema(z.object(repRangeShape).optional());
230
+ optionalRepRangeSchema(z.object(repRangeShape).strict().optional());
227
231
  const setTypeEnum = z.enum([
228
232
  "warmup",
229
233
  "normal",
@@ -1502,7 +1506,7 @@ function registerWorkoutPrompts(server, observer) {
1502
1506
  server.registerPrompt("analyze-workout-progress", {
1503
1507
  title: "Analyze Workout Progress",
1504
1508
  description: "Analyze recent workout and body-measurement trends.",
1505
- argsSchema: { weeks: z.coerce.number().int().min(1).max(12).default(4).optional().describe("Number of recent weeks to analyze (1-12).") }
1509
+ argsSchema: z.object({ weeks: z.coerce.number().int().min(1).max(12).default(4).optional().describe("Number of recent weeks to analyze (1-12).") })
1506
1510
  }, withPromptObservation("analyze-workout-progress", observer, ({ weeks = 4 }) => ({ messages: [{
1507
1511
  role: "user",
1508
1512
  content: {
@@ -1519,10 +1523,10 @@ function registerWorkoutPrompts(server, observer) {
1519
1523
  server.registerPrompt("create-workout-from-routine", {
1520
1524
  title: "Create Workout From Routine",
1521
1525
  description: "Create a completed workout from an existing routine.",
1522
- argsSchema: {
1526
+ argsSchema: z.object({
1523
1527
  routineId: z.string().min(1).optional().describe("Routine ID to use as a guide."),
1524
1528
  startTime: utcSecondTimestamp.optional().describe("Workout start time in UTC as YYYY-MM-DDTHH:mm:ssZ.")
1525
- }
1529
+ })
1526
1530
  }, withPromptObservation("create-workout-from-routine", observer, ({ routineId, startTime }) => ({ messages: [{
1527
1531
  role: "user",
1528
1532
  content: {
@@ -2595,7 +2599,7 @@ function registerHevyResources(server, runtime) {
2595
2599
  //#endregion
2596
2600
  //#region ../core/src/server-metadata.ts
2597
2601
  const SERVER_NAME = "hevy-mcp";
2598
- const SERVER_VERSION = "4.1.1";
2602
+ const SERVER_VERSION = "4.1.3";
2599
2603
  const SERVER_INSTRUCTIONS = [
2600
2604
  "Hevy MCP connects clients to the authenticated user's Hevy workout-tracking data, including workouts, routines, exercise templates, routine folders, body measurements, and profile information. HEVY_API_KEY must contain a valid Hevy API key for local stdio use.",
2601
2605
  "Safety: all get-* and search-* tools are read-only. create-* and update-* tools mutate Hevy data. Creates are additive and non-idempotent, so repeating one can create duplicates. Updates can overwrite existing data. Delete operations are not available.",
@@ -2655,16 +2659,13 @@ function registerToolDefinition(server, runtime, definition) {
2655
2659
  kind: definition.kind,
2656
2660
  operation: definition.operation
2657
2661
  });
2658
- if (definition.kind === "read") {
2659
- server.registerTool(definition.name, {
2660
- description: definition.description,
2661
- inputSchema: definition.inputSchema,
2662
- outputSchema: definition.outputSchema,
2663
- annotations: definition.annotations
2664
- }, callback);
2665
- return;
2666
- }
2667
- server.tool(definition.name, definition.description, definition.inputSchema, definition.annotations, callback);
2662
+ const config = {
2663
+ description: definition.description,
2664
+ inputSchema: z.object(definition.inputSchema),
2665
+ annotations: definition.annotations,
2666
+ ...definition.kind === "read" ? { outputSchema: z.object(definition.outputSchema) } : {}
2667
+ };
2668
+ server.registerTool(definition.name, config, callback);
2668
2669
  }
2669
2670
  //#endregion
2670
2671
  //#region ../core/src/utils/tool-annotations.ts
@@ -2740,6 +2741,14 @@ function paginationShape({ defaultPageSize, maxPageSize, integerPage = true }) {
2740
2741
  };
2741
2742
  }
2742
2743
  const nonEmptyId = z.string().min(1);
2744
+ const exerciseTemplateInputShape = {
2745
+ title: z.string().min(1),
2746
+ exerciseType: exerciseTypeEnum,
2747
+ equipmentCategory: equipmentCategoryEnum,
2748
+ muscleGroup: muscleGroupEnum,
2749
+ otherMuscles: z.array(muscleGroupEnum).default([])
2750
+ };
2751
+ const routineFolderInputShape = { name: z.string().min(1) };
2743
2752
  const CALENDAR_DATE_MESSAGE = "Date must be in YYYY-MM-DD format";
2744
2753
  const calendarDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, CALENDAR_DATE_MESSAGE).refine((value) => {
2745
2754
  const parsed = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
@@ -2940,6 +2949,18 @@ function buildMeasurementPayload(fields) {
2940
2949
  }
2941
2950
  return payload;
2942
2951
  }
2952
+ function buildExerciseTemplateRequest(input) {
2953
+ return { exercise: {
2954
+ title: input.title,
2955
+ exercise_type: input.exerciseType,
2956
+ equipment_category: input.equipmentCategory,
2957
+ muscle_group: input.muscleGroup,
2958
+ other_muscles: input.otherMuscles
2959
+ } };
2960
+ }
2961
+ function buildRoutineFolderRequest(input) {
2962
+ return { routine_folder: { title: input.name } };
2963
+ }
2943
2964
  //#endregion
2944
2965
  //#region ../core/src/utils/hevy-error-policy.ts
2945
2966
  const READ_RESOURCE_ENDPOINTS = /* @__PURE__ */ new Set([
@@ -3119,7 +3140,7 @@ const getRoutineFoldersSchema = paginationShape({
3119
3140
  maxPageSize: 10
3120
3141
  });
3121
3142
  const getRoutineFolderSchema = { folderId: nonEmptyId };
3122
- const createRoutineFolderSchema = { name: z.string().min(1) };
3143
+ const createRoutineFolderSchema = routineFolderInputShape;
3123
3144
  const folderToolDefinitions = [
3124
3145
  {
3125
3146
  name: "get-routine-folders",
@@ -3217,8 +3238,7 @@ const folderToolDefinitions = [
3217
3238
  kind: "write",
3218
3239
  responseContract: createRoutineFolderResponse,
3219
3240
  execute: async (runtime, args) => {
3220
- const { name } = args;
3221
- return runtime.getClient().createRoutineFolder({ routine_folder: { title: name } });
3241
+ return runtime.getClient().createRoutineFolder(buildRoutineFolderRequest(args));
3222
3242
  }
3223
3243
  }
3224
3244
  ];
@@ -3383,13 +3403,7 @@ const getExerciseHistorySchema = {
3383
3403
  startDate: z.string().datetime({ offset: true }).describe("ISO 8601 start date for filtering history").optional(),
3384
3404
  endDate: z.string().datetime({ offset: true }).describe("ISO 8601 end date for filtering history").optional()
3385
3405
  };
3386
- const createExerciseTemplateSchema = {
3387
- title: z.string().min(1),
3388
- exerciseType: exerciseTypeEnum,
3389
- equipmentCategory: equipmentCategoryEnum,
3390
- muscleGroup: muscleGroupEnum,
3391
- otherMuscles: z.array(muscleGroupEnum).default([])
3392
- };
3406
+ const createExerciseTemplateSchema = exerciseTemplateInputShape;
3393
3407
  const searchExerciseTemplatesSchema = {
3394
3408
  query: z.string().min(1).describe("Case-insensitive substring to match against exercise template titles"),
3395
3409
  primaryMuscleGroup: muscleGroupEnum.optional().describe("Optional filter to restrict results to a specific primary muscle group"),
@@ -3523,14 +3537,7 @@ const templateToolDefinitions = [
3523
3537
  kind: "write",
3524
3538
  responseContract: createExerciseTemplateResponse,
3525
3539
  execute: async (runtime, args) => {
3526
- const { title, exerciseType, equipmentCategory, muscleGroup, otherMuscles } = args;
3527
- return await runtime.getClient().createExerciseTemplate({ exercise: {
3528
- title,
3529
- exercise_type: exerciseType,
3530
- equipment_category: equipmentCategory,
3531
- muscle_group: muscleGroup,
3532
- other_muscles: otherMuscles
3533
- } });
3540
+ return runtime.getClient().createExerciseTemplate(buildExerciseTemplateRequest(args));
3534
3541
  }
3535
3542
  },
3536
3543
  {
@@ -4388,18 +4395,13 @@ function createCountingServer(server) {
4388
4395
  let count = 0;
4389
4396
  return {
4390
4397
  server: new Proxy(server, { get(target, property, receiver) {
4391
- if (property === "tool") return (...args) => {
4392
- const result = target.tool(...args);
4393
- count += 1;
4394
- return result;
4395
- };
4396
4398
  if (property === "registerTool") {
4397
- const registerTool = (name, config, callback) => {
4398
- const result = target.registerTool(name, config, callback);
4399
+ const registerTool = target.registerTool.bind(target);
4400
+ return (...args) => {
4401
+ const result = Reflect.apply(registerTool, target, args);
4399
4402
  count += 1;
4400
4403
  return result;
4401
4404
  };
4402
- return registerTool;
4403
4405
  }
4404
4406
  return Reflect.get(target, property, receiver);
4405
4407
  } }),
@@ -4801,7 +4803,7 @@ async function startStreamableHttpServer(options, apiKey, createMcpServer) {
4801
4803
  let session;
4802
4804
  let mcpServer;
4803
4805
  let connected = false;
4804
- const transport = new StreamableHTTPServerTransport({
4806
+ const transport = new NodeStreamableHTTPServerTransport({
4805
4807
  sessionIdGenerator: randomUUID,
4806
4808
  onsessioninitialized: (id) => {
4807
4809
  if (session) sessions.set(id, session);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hevy-mcp",
3
- "version": "4.1.1",
3
+ "version": "4.1.3",
4
4
  "private": false,
5
5
  "description": "A Model Context Protocol (MCP) server implementation that interfaces with the Hevy fitness tracking app and its API.",
6
6
  "repository": {
@@ -34,25 +34,26 @@
34
34
  "build:standalone": "cross-env HEVY_MCP_BUILD_MODE=standalone tsdown -c tsdown.config.ts"
35
35
  },
36
36
  "dependencies": {
37
- "@modelcontextprotocol/sdk": "^1.29.0",
38
- "@opentelemetry/api": "^1.9.0",
39
- "@opentelemetry/exporter-metrics-otlp-http": "^0.220.0",
40
- "@opentelemetry/exporter-trace-otlp-http": "^0.220.0",
41
- "@opentelemetry/resources": "^2.9.0",
42
- "@opentelemetry/sdk-metrics": "^2.9.0",
43
- "@opentelemetry/sdk-trace-base": "^2.9.0",
44
- "@opentelemetry/sdk-trace-node": "^2.9.0",
45
- "@sentry/node": "^10.58.0",
46
- "@sentry/opentelemetry": "^10.58.0",
37
+ "@modelcontextprotocol/node": "^2.0.0",
38
+ "@modelcontextprotocol/server": "^2.0.0",
39
+ "@opentelemetry/api": "^1.9.1",
40
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.221.0",
41
+ "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
42
+ "@opentelemetry/resources": "^2.10.0",
43
+ "@opentelemetry/sdk-metrics": "^2.10.0",
44
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
45
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
46
+ "@sentry/node": "^10.68.0",
47
+ "@sentry/opentelemetry": "^10.68.0",
47
48
  "semver": "^7.8.5",
48
49
  "zod": "^4.4.3"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@hevy-mcp/core": "*",
52
53
  "@hevy-mcp/hevy-client": "*",
53
- "@types/node": "^26.0.0",
54
+ "@types/node": "^26.1.1",
54
55
  "cross-env": "^10.1.0",
55
- "tsdown": "^0.22.2",
56
+ "tsdown": "^0.22.14",
56
57
  "typescript": "^7.0.2"
57
58
  },
58
59
  "engines": {
package/server.json CHANGED
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/chrisdoc/hevy-mcp",
8
8
  "source": "github"
9
9
  },
10
- "version": "4.1.1",
10
+ "version": "4.1.3",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "hevy-mcp",
15
- "version": "4.1.1",
15
+ "version": "4.1.3",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  },