hevy-mcp 5.0.0 → 5.0.2
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 +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{src-BUmpaxmH.mjs → src-DBkGttYB.mjs} +67 -18
- package/package.json +1 -1
- package/server.json +2 -2
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@5.0.
|
|
7
|
+
e.SENTRY_RELEASE = { id: "hevy-mcp@5.0.2" };
|
|
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-
|
|
12
|
+
import { a as createSafeErrorDiagnostic, i as MissingHevyApiKeyError, n as runServer, o as flushTelemetry } from "./src-DBkGttYB.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.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@5.0.
|
|
7
|
+
e.SENTRY_RELEASE = { id: "hevy-mcp@5.0.2" };
|
|
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-
|
|
12
|
+
import { n as runServer, r as runStdioServer, t as createNodeMcpServer } from "./src-DBkGttYB.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@5.0.
|
|
7
|
+
e.SENTRY_RELEASE = { id: "hevy-mcp@5.0.2" };
|
|
8
8
|
var n = new e.Error().stack;
|
|
9
|
-
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "
|
|
9
|
+
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "f7cbf1da-83d1-4da1-bb18-1c590afb9b11", e._sentryDebugIdIdentifier = "sentry-dbid-f7cbf1da-83d1-4da1-bb18-1c590afb9b11");
|
|
10
10
|
} catch (e) {}
|
|
11
11
|
})();
|
|
12
12
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -20,7 +20,7 @@ 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
22
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
23
|
-
import { z } from "zod";
|
|
23
|
+
import { ZodError, z } from "zod";
|
|
24
24
|
import { McpServer, deserializeMessage } from "@modelcontextprotocol/server";
|
|
25
25
|
import { z as z$1 } from "zod/v4";
|
|
26
26
|
import { isIP } from "node:net";
|
|
@@ -80,11 +80,11 @@ function sanitizeSentryMcpSpan(span) {
|
|
|
80
80
|
* OTel Collector → Honeycomb: performance traces, metrics
|
|
81
81
|
*/
|
|
82
82
|
const name$1 = "hevy-mcp";
|
|
83
|
-
const version$1 = "5.0.
|
|
83
|
+
const version$1 = "5.0.2";
|
|
84
84
|
const telemetryEnabled = process.env.HEVY_MCP_TELEMETRY !== "0";
|
|
85
85
|
const collectorToken = "NH9vOela-HYreQxAbJa68cjEmORoEKM57EvneUDVcOo";
|
|
86
86
|
const COLLECTOR_ENDPOINT = "https://otel.chrisdoc.dev/v1";
|
|
87
|
-
const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@5.0.
|
|
87
|
+
const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@5.0.2`;
|
|
88
88
|
const resource = resourceFromAttributes({
|
|
89
89
|
"service.name": name$1,
|
|
90
90
|
"service.version": version$1
|
|
@@ -985,6 +985,28 @@ function normalizeMaxGetRetries(value) {
|
|
|
985
985
|
function defaultSleep(milliseconds) {
|
|
986
986
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
987
987
|
}
|
|
988
|
+
function withTimeout(operation, timeoutMs, onTimeout) {
|
|
989
|
+
return new Promise((resolve, reject) => {
|
|
990
|
+
let settled = false;
|
|
991
|
+
const timer = setTimeout(() => {
|
|
992
|
+
if (settled) return;
|
|
993
|
+
settled = true;
|
|
994
|
+
onTimeout();
|
|
995
|
+
reject(new DOMException("Operation timed out", "AbortError"));
|
|
996
|
+
}, Math.max(0, timeoutMs));
|
|
997
|
+
operation.then((value) => {
|
|
998
|
+
if (settled) return;
|
|
999
|
+
settled = true;
|
|
1000
|
+
clearTimeout(timer);
|
|
1001
|
+
resolve(value);
|
|
1002
|
+
}, (error) => {
|
|
1003
|
+
if (settled) return;
|
|
1004
|
+
settled = true;
|
|
1005
|
+
clearTimeout(timer);
|
|
1006
|
+
reject(error);
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
988
1010
|
function getRequestContext(config) {
|
|
989
1011
|
const method = (config.method ?? "GET").toUpperCase();
|
|
990
1012
|
const rawEndpoint = (config.url ?? "").split("?")[0] ?? "";
|
|
@@ -1081,14 +1103,15 @@ function createNativeClient(apiKey, baseUrl, options) {
|
|
|
1081
1103
|
try {
|
|
1082
1104
|
const headers = new Headers({ "api-key": apiKey });
|
|
1083
1105
|
if (normalized.data !== void 0 && !(normalized.data instanceof FormData)) headers.set("content-type", "application/json");
|
|
1084
|
-
const
|
|
1106
|
+
const attemptDeadline = Date.now() + timeoutMs;
|
|
1107
|
+
const response = await withTimeout(fetchImplementation(url, {
|
|
1085
1108
|
method,
|
|
1086
1109
|
headers,
|
|
1087
1110
|
redirect: "manual",
|
|
1088
1111
|
body: normalized.data instanceof FormData ? normalized.data : normalized.data === void 0 ? void 0 : JSON.stringify(normalized.data),
|
|
1089
1112
|
signal: controller.signal
|
|
1090
|
-
});
|
|
1091
|
-
const data = await parseResponseData(response);
|
|
1113
|
+
}), Math.max(0, attemptDeadline - Date.now()), () => controller.abort());
|
|
1114
|
+
const data = await withTimeout(parseResponseData(response), Math.max(0, attemptDeadline - Date.now()), () => controller.abort());
|
|
1092
1115
|
if (!response.ok) throw new HevyHttpError(`Hevy API request failed (HTTP ${response.status})`, {
|
|
1093
1116
|
status: response.status,
|
|
1094
1117
|
statusText: response.statusText,
|
|
@@ -1691,7 +1714,7 @@ const routineSchema = z$1.object({
|
|
|
1691
1714
|
exercises: z$1.optional(z$1.array(z$1.object({
|
|
1692
1715
|
index: z$1.optional(z$1.number().describe("Index indicating the order of the exercise in the routine.")),
|
|
1693
1716
|
title: z$1.optional(z$1.string().describe("Title of the exercise")),
|
|
1694
|
-
rest_seconds: z$1.optional(z$1.
|
|
1717
|
+
rest_seconds: z$1.optional(z$1.int().describe("The rest time in seconds between sets of the exercise")),
|
|
1695
1718
|
notes: z$1.optional(z$1.string().describe("Routine notes on the exercise")),
|
|
1696
1719
|
exercise_template_id: z$1.optional(z$1.string().describe("The id of the exercise template. This can be used to fetch the exercise template.")),
|
|
1697
1720
|
supersets_id: z$1.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(),
|
|
@@ -1934,7 +1957,7 @@ const formattedRoutineExerciseSchema = z.object({
|
|
|
1934
1957
|
exercise_template_id: z.string().optional(),
|
|
1935
1958
|
notes: z.string().optional(),
|
|
1936
1959
|
supersets_id: optionalNumber,
|
|
1937
|
-
rest_seconds: z.
|
|
1960
|
+
rest_seconds: z.number().int().optional(),
|
|
1938
1961
|
sets: z.array(formattedRoutineSetSchema).optional()
|
|
1939
1962
|
});
|
|
1940
1963
|
const formattedRoutineSchema = z.object({
|
|
@@ -2621,7 +2644,7 @@ function registerHevyResources(server, runtime) {
|
|
|
2621
2644
|
//#endregion
|
|
2622
2645
|
//#region ../core/src/server-metadata.ts
|
|
2623
2646
|
const SERVER_NAME = "hevy-mcp";
|
|
2624
|
-
const SERVER_VERSION = "5.0.
|
|
2647
|
+
const SERVER_VERSION = "5.0.2";
|
|
2625
2648
|
const SERVER_INSTRUCTIONS = [
|
|
2626
2649
|
"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.",
|
|
2627
2650
|
"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.",
|
|
@@ -5152,6 +5175,10 @@ const SAFE_MCP_METHODS = {
|
|
|
5152
5175
|
"prompts/list": true
|
|
5153
5176
|
};
|
|
5154
5177
|
const REDACTED_CONTENT_MARKER = "[REDACTED]";
|
|
5178
|
+
const MAX_MALFORMED_LINES_PER_READ = 100;
|
|
5179
|
+
function isMalformedMessageError(error) {
|
|
5180
|
+
return error instanceof SyntaxError || error instanceof ZodError;
|
|
5181
|
+
}
|
|
5155
5182
|
/**
|
|
5156
5183
|
* Adapter boundary around MCP SDK stdio internals.
|
|
5157
5184
|
*
|
|
@@ -5177,14 +5204,36 @@ function createSdkPrivateStdioAdapter(transport) {
|
|
|
5177
5204
|
installReadMessageHook(onReadLine) {
|
|
5178
5205
|
const readBuffer = mutableTransport._readBuffer;
|
|
5179
5206
|
if (!readBuffer || typeof readBuffer.readMessage !== "function") return false;
|
|
5207
|
+
let deferredMessage = null;
|
|
5180
5208
|
readBuffer.readMessage = () => {
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5209
|
+
if (deferredMessage) {
|
|
5210
|
+
const message = deferredMessage;
|
|
5211
|
+
deferredMessage = null;
|
|
5212
|
+
return message;
|
|
5213
|
+
}
|
|
5214
|
+
let skippedMalformedLines = 0;
|
|
5215
|
+
while (true) {
|
|
5216
|
+
const buffer = readBuffer._buffer;
|
|
5217
|
+
if (!buffer) return null;
|
|
5218
|
+
const index = buffer.indexOf("\n");
|
|
5219
|
+
if (index === -1) return null;
|
|
5220
|
+
const lineBuffer = buffer.subarray(0, index);
|
|
5221
|
+
readBuffer._buffer = buffer.subarray(index + 1);
|
|
5222
|
+
const line = lineBuffer.toString("utf8").replace(/\r$/, "");
|
|
5223
|
+
try {
|
|
5224
|
+
return onReadLine(line);
|
|
5225
|
+
} catch (error) {
|
|
5226
|
+
if (!isMalformedMessageError(error)) throw error;
|
|
5227
|
+
skippedMalformedLines += 1;
|
|
5228
|
+
if (skippedMalformedLines >= MAX_MALFORMED_LINES_PER_READ) {
|
|
5229
|
+
setImmediate(() => {
|
|
5230
|
+
const message = readBuffer.readMessage();
|
|
5231
|
+
if (message) deferredMessage = message;
|
|
5232
|
+
});
|
|
5233
|
+
return null;
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
5188
5237
|
};
|
|
5189
5238
|
return true;
|
|
5190
5239
|
}
|
package/package.json
CHANGED
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": "5.0.
|
|
10
|
+
"version": "5.0.2",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "hevy-mcp",
|
|
15
|
-
"version": "5.0.
|
|
15
|
+
"version": "5.0.2",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|