hevy-mcp 1.26.4 → 1.26.6

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@1.26.4" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.6" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "03cf150f-6466-4ca3-9cf1-e423cc0e3cd2", e._sentryDebugIdIdentifier = "sentry-dbid-03cf150f-6466-4ca3-9cf1-e423cc0e3cd2");
10
10
  } catch (e) {}
11
11
  })();
12
- import { r as runServer } from "./src-DSiZLCgu.mjs";
12
+ import { r as runServer } from "./src-B9KZiN70.mjs";
13
13
  //#region src/cli.ts
14
14
  runServer().catch((error) => {
15
15
  console.error("Fatal error in main():", error);
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@1.26.4" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.6" };
8
8
  var n = new e.Error().stack;
9
9
  n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "5b077b94-9ef8-4f24-a0b0-8dcfbf491be1", e._sentryDebugIdIdentifier = "sentry-dbid-5b077b94-9ef8-4f24-a0b0-8dcfbf491be1");
10
10
  } catch (e) {}
11
11
  })();
12
- import { n as createServer, r as runServer, t as configSchema } from "./src-DSiZLCgu.mjs";
12
+ import { n as createServer, r as runServer, t as configSchema } from "./src-B9KZiN70.mjs";
13
13
  export { configSchema, createServer, createServer as default, runServer };
@@ -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@1.26.4" };
7
+ e.SENTRY_RELEASE = { id: "hevy-mcp@1.26.6" };
8
8
  var n = new e.Error().stack;
9
- n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "38ab92ce-2e6e-45ef-bcda-d9cbe2100f4a", e._sentryDebugIdIdentifier = "sentry-dbid-38ab92ce-2e6e-45ef-bcda-d9cbe2100f4a");
9
+ n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "523f4f6a-5f55-463e-ace9-ee2022739238", e._sentryDebugIdIdentifier = "sentry-dbid-523f4f6a-5f55-463e-ace9-ee2022739238");
10
10
  } catch (e) {}
11
11
  })();
12
12
  import * as Sentry from "@sentry/node";
@@ -36,10 +36,10 @@ import { deserializeMessage } from "@modelcontextprotocol/sdk/shared/stdio.js";
36
36
  * OTel Collector → Honeycomb: performance traces, metrics
37
37
  */
38
38
  const name$1 = "hevy-mcp";
39
- const version$1 = "1.26.4";
40
- const collectorToken = process.env.OTEL_COLLECTOR_TOKEN ?? "";
39
+ const version$1 = "1.26.6";
40
+ const collectorToken = "NH9vOela-HYreQxAbJa68cjEmORoEKM57EvneUDVcOo";
41
41
  const COLLECTOR_ENDPOINT = "https://otel.chrisdoc.dev/v1";
42
- const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@1.26.4`;
42
+ const sentryRelease = process.env.SENTRY_RELEASE ?? `hevy-mcp@1.26.6`;
43
43
  const resource = resourceFromAttributes({
44
44
  "service.name": name$1,
45
45
  "service.version": version$1
@@ -56,7 +56,7 @@ const sentryClient = Sentry.init({
56
56
  ignoreErrors: ["EPIPE", "broken pipe"]
57
57
  });
58
58
  const spanProcessors = [new SentrySpanProcessor()];
59
- if (collectorToken) spanProcessors.push(new BatchSpanProcessor(new OTLPTraceExporter({
59
+ spanProcessors.push(new BatchSpanProcessor(new OTLPTraceExporter({
60
60
  url: `${COLLECTOR_ENDPOINT}/traces`,
61
61
  headers: { Authorization: `Bearer ${collectorToken}` }
62
62
  })));
@@ -69,7 +69,7 @@ tracerProvider.register({
69
69
  propagator: new SentryPropagator(),
70
70
  contextManager: new Sentry.SentryContextManager()
71
71
  });
72
- if (collectorToken) {
72
+ {
73
73
  const meterProvider = new MeterProvider({
74
74
  resource,
75
75
  readers: [new PeriodicExportingMetricReader({
@@ -258,6 +258,40 @@ function determineErrorType(error, message) {
258
258
  if (nameLower.includes("api") || messageLower.includes("api") || messageLower.includes("server error") || messageLower.includes("500")) return "API_ERROR";
259
259
  return "UNKNOWN_ERROR";
260
260
  }
261
+ /** Whitelist of safe argument keys that can be logged as span attributes without exposing PII. */
262
+ const ARGUMENT_WHITELIST = /* @__PURE__ */ new Set([
263
+ "page",
264
+ "pageSize",
265
+ "since",
266
+ "workoutId",
267
+ "routineId",
268
+ "folderId",
269
+ "exerciseTemplateId",
270
+ "date",
271
+ "startDate",
272
+ "endDate",
273
+ "updatedSince",
274
+ "includeCustom",
275
+ "limit",
276
+ "offset",
277
+ "refresh",
278
+ "query",
279
+ "primaryMuscleGroup"
280
+ ]);
281
+ /**
282
+ * Extract safe (non-PII) parameters from the tool arguments to include as span attributes.
283
+ */
284
+ function extractSafeArgs(args) {
285
+ const attributes = {};
286
+ for (const [key, value] of Object.entries(args)) if (ARGUMENT_WHITELIST.has(key)) {
287
+ const type = typeof value;
288
+ if (type === "string" || type === "number" || type === "boolean") if (key === "query" && type === "string") {
289
+ const strVal = value;
290
+ attributes[`mcp.tool.args.${key}`] = strVal.length > 100 ? `${strVal.slice(0, 100)}...` : strVal;
291
+ } else attributes[`mcp.tool.args.${key}`] = value;
292
+ }
293
+ return attributes;
294
+ }
261
295
  /**
262
296
  * Wrap an async function with standardized error handling
263
297
  *
@@ -270,25 +304,41 @@ function determineErrorType(error, message) {
270
304
  * @returns A function that catches errors and returns standardized error responses
271
305
  */
272
306
  function withErrorHandling(fn, context) {
273
- return async (args) => {
307
+ return async (rawArgs) => {
308
+ const args = rawArgs ?? {};
274
309
  const argumentKeyCount = Object.keys(args).length;
275
310
  const startTime = Date.now();
276
311
  toolInvocations.add(1, { tool_name: context });
312
+ const userId = getCurrentUserId();
313
+ const safeArgs = extractSafeArgs(args);
314
+ const whitelistedKeys = Object.keys(safeArgs).map((k) => k.replace("mcp.tool.args.", ""));
277
315
  return tracer.startActiveSpan(`mcp.tool.${context}`, { attributes: {
278
316
  "mcp.tool.name": context,
279
317
  "mcp.tool.args.key_count": argumentKeyCount,
280
- ...getCurrentUserId() ? { "user.id": getCurrentUserId() } : {}
318
+ "mcp.tool.args.keys": whitelistedKeys.join(","),
319
+ ...userId ? { "user.id": userId } : {},
320
+ ...safeArgs
281
321
  } }, async (span) => {
322
+ let isError = false;
282
323
  try {
283
324
  const result = await fn(args);
284
- span.setStatus({ code: SpanStatusCode.OK });
285
- span.setAttribute("mcp.tool.result.is_error", Boolean(result.isError));
325
+ isError = Boolean(result.isError);
326
+ span.setStatus({ code: isError ? SpanStatusCode.ERROR : SpanStatusCode.OK });
327
+ span.setAttribute("mcp.tool.result.is_error", isError);
328
+ if (result.content) {
329
+ span.setAttribute("mcp.tool.result.content_count", result.content.length);
330
+ const textLength = result.content.reduce((sum, item) => sum + (item.text?.length ?? 0), 0);
331
+ span.setAttribute("mcp.tool.result.text_length", textLength);
332
+ }
286
333
  return result;
287
334
  } catch (error) {
335
+ isError = true;
288
336
  span.setStatus({ code: SpanStatusCode.ERROR });
289
337
  span.recordException(error);
290
338
  const errorType = determineErrorType(error, error instanceof Error ? error.message : String(error));
291
339
  span.setAttribute("error.type", errorType);
340
+ const rawCode = error instanceof Error && "code" in error ? error.code : void 0;
341
+ if (rawCode !== void 0 && rawCode !== null) span.setAttribute("error.code", String(rawCode));
292
342
  toolErrors.add(1, {
293
343
  tool_name: context,
294
344
  error_type: errorType
@@ -303,7 +353,10 @@ function withErrorHandling(fn, context) {
303
353
  });
304
354
  return createErrorResponse(error, context);
305
355
  } finally {
306
- toolDuration.record(Date.now() - startTime, { tool_name: context });
356
+ toolDuration.record(Date.now() - startTime, {
357
+ tool_name: context,
358
+ is_error: String(isError)
359
+ });
307
360
  span.end();
308
361
  }
309
362
  });
@@ -2371,7 +2424,7 @@ function getCliAction(args) {
2371
2424
  const HEVY_API_BASEURL = "https://api.hevyapp.com";
2372
2425
  const SENTRY_USER_ID_CONTEXT = "hevy-mcp:sentry-user-id:v1";
2373
2426
  function fingerprintApiKey(apiKey) {
2374
- return createHmac("sha256", apiKey).update(SENTRY_USER_ID_CONTEXT).digest("hex");
2427
+ return createHmac("sha256", apiKey).update(SENTRY_USER_ID_CONTEXT).digest("hex").slice(0, 10);
2375
2428
  }
2376
2429
  const serverConfigSchema = z.object({ apiKey: z.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).") });
2377
2430
  const configSchema = serverConfigSchema;
@@ -2467,4 +2520,4 @@ async function runServer() {
2467
2520
  //#endregion
2468
2521
  export { createServer as n, runServer as r, configSchema as t };
2469
2522
 
2470
- //# sourceMappingURL=src-DSiZLCgu.mjs.map
2523
+ //# sourceMappingURL=src-B9KZiN70.mjs.map