hevy-mcp 1.13.1 → 1.14.0
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/README.md +16 -1
- package/dist/cli.js +45 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.js +45 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -95,6 +95,21 @@ Replace `your_hevy_api_key_here` with your actual Hevy API key. If you prefer th
|
|
|
95
95
|
pnpm start -- --hevy-api-key=your_hevy_api_key_here
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### Sentry monitoring
|
|
99
|
+
|
|
100
|
+
`hevy-mcp` ships with Sentry monitoring baked into the built MCP server so
|
|
101
|
+
that usage and errors from published builds can be observed.
|
|
102
|
+
|
|
103
|
+
The server initializes `@sentry/node` with a fixed DSN and tracing settings
|
|
104
|
+
directly in the code (see `src/index.ts`), and wraps the underlying
|
|
105
|
+
`McpServer` with `Sentry.wrapMcpServerWithSentry` so requests and tool calls
|
|
106
|
+
are captured by Sentry automatically. The configuration uses
|
|
107
|
+
`sendDefaultPii: false` to keep Sentry's default PII collection disabled.
|
|
108
|
+
|
|
109
|
+
There is currently no built-in toggle to disable Sentry for the published
|
|
110
|
+
package. If you need a build without Sentry telemetry, you can fork the
|
|
111
|
+
repository and remove the Sentry initialization in `src/index.ts`.
|
|
112
|
+
|
|
98
113
|
## Transport
|
|
99
114
|
|
|
100
115
|
### Deploy via Smithery (TypeScript runtime)
|
|
@@ -299,4 +314,4 @@ Contributions are welcome! Please feel free to submit a Pull Request. For major
|
|
|
299
314
|
## Acknowledgements
|
|
300
315
|
|
|
301
316
|
- [Model Context Protocol](https://github.com/modelcontextprotocol) for the MCP SDK
|
|
302
|
-
- [Hevy](https://www.hevyapp.com/) for their fitness tracking platform and API
|
|
317
|
+
- [Hevy](https://www.hevyapp.com/) for their fitness tracking platform and API
|
package/dist/cli.js
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
import dotenvx from "@dotenvx/dotenvx";
|
|
7
|
+
import * as Sentry from "@sentry/node";
|
|
7
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
9
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
10
|
import { z as z6 } from "zod";
|
|
10
11
|
|
|
11
12
|
// package.json
|
|
12
13
|
var name = "hevy-mcp";
|
|
13
|
-
var version = "1.13.
|
|
14
|
+
var version = "1.13.2";
|
|
14
15
|
|
|
15
16
|
// src/tools/folders.ts
|
|
16
17
|
import { z } from "zod";
|
|
@@ -343,9 +344,12 @@ function registerRoutineTools(server, hevyClient) {
|
|
|
343
344
|
sets: z2.array(
|
|
344
345
|
z2.object({
|
|
345
346
|
type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
|
|
347
|
+
weight: z2.coerce.number().optional(),
|
|
346
348
|
weightKg: z2.coerce.number().optional(),
|
|
347
349
|
reps: z2.coerce.number().int().optional(),
|
|
350
|
+
distance: z2.coerce.number().int().optional(),
|
|
348
351
|
distanceMeters: z2.coerce.number().int().optional(),
|
|
352
|
+
duration: z2.coerce.number().int().optional(),
|
|
349
353
|
durationSeconds: z2.coerce.number().int().optional(),
|
|
350
354
|
customMetric: z2.coerce.number().optional()
|
|
351
355
|
})
|
|
@@ -378,10 +382,10 @@ function registerRoutineTools(server, hevyClient) {
|
|
|
378
382
|
sets: exercise.sets.map(
|
|
379
383
|
(set) => ({
|
|
380
384
|
type: set.type,
|
|
381
|
-
weight_kg: set.weightKg ?? null,
|
|
385
|
+
weight_kg: set.weight ?? set.weightKg ?? null,
|
|
382
386
|
reps: set.reps ?? null,
|
|
383
|
-
distance_meters: set.distanceMeters ?? null,
|
|
384
|
-
duration_seconds: set.durationSeconds ?? null,
|
|
387
|
+
distance_meters: set.distance ?? set.distanceMeters ?? null,
|
|
388
|
+
duration_seconds: set.duration ?? set.durationSeconds ?? null,
|
|
385
389
|
custom_metric: set.customMetric ?? null
|
|
386
390
|
})
|
|
387
391
|
)
|
|
@@ -414,9 +418,12 @@ function registerRoutineTools(server, hevyClient) {
|
|
|
414
418
|
sets: z2.array(
|
|
415
419
|
z2.object({
|
|
416
420
|
type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
|
|
421
|
+
weight: z2.coerce.number().optional(),
|
|
417
422
|
weightKg: z2.coerce.number().optional(),
|
|
418
423
|
reps: z2.coerce.number().int().optional(),
|
|
424
|
+
distance: z2.coerce.number().int().optional(),
|
|
419
425
|
distanceMeters: z2.coerce.number().int().optional(),
|
|
426
|
+
duration: z2.coerce.number().int().optional(),
|
|
420
427
|
durationSeconds: z2.coerce.number().int().optional(),
|
|
421
428
|
customMetric: z2.coerce.number().optional()
|
|
422
429
|
})
|
|
@@ -448,10 +455,10 @@ function registerRoutineTools(server, hevyClient) {
|
|
|
448
455
|
sets: exercise.sets.map(
|
|
449
456
|
(set) => ({
|
|
450
457
|
type: set.type,
|
|
451
|
-
weight_kg: set.weightKg ?? null,
|
|
458
|
+
weight_kg: set.weight ?? set.weightKg ?? null,
|
|
452
459
|
reps: set.reps ?? null,
|
|
453
|
-
distance_meters: set.distanceMeters ?? null,
|
|
454
|
-
duration_seconds: set.durationSeconds ?? null,
|
|
460
|
+
distance_meters: set.distance ?? set.distanceMeters ?? null,
|
|
461
|
+
duration_seconds: set.duration ?? set.durationSeconds ?? null,
|
|
455
462
|
custom_metric: set.customMetric ?? null
|
|
456
463
|
})
|
|
457
464
|
)
|
|
@@ -762,9 +769,12 @@ function registerWorkoutTools(server, hevyClient) {
|
|
|
762
769
|
sets: z5.array(
|
|
763
770
|
z5.object({
|
|
764
771
|
type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
|
|
772
|
+
weight: z5.coerce.number().optional().nullable(),
|
|
765
773
|
weightKg: z5.coerce.number().optional().nullable(),
|
|
766
774
|
reps: z5.coerce.number().int().optional().nullable(),
|
|
775
|
+
distance: z5.coerce.number().int().optional().nullable(),
|
|
767
776
|
distanceMeters: z5.coerce.number().int().optional().nullable(),
|
|
777
|
+
duration: z5.coerce.number().int().optional().nullable(),
|
|
768
778
|
durationSeconds: z5.coerce.number().int().optional().nullable(),
|
|
769
779
|
rpe: z5.coerce.number().optional().nullable(),
|
|
770
780
|
customMetric: z5.coerce.number().optional().nullable()
|
|
@@ -794,16 +804,16 @@ function registerWorkoutTools(server, hevyClient) {
|
|
|
794
804
|
exercises: exercises.map(
|
|
795
805
|
(exercise) => ({
|
|
796
806
|
exercise_template_id: exercise.exerciseTemplateId,
|
|
797
|
-
superset_id: exercise.supersetId
|
|
798
|
-
notes: exercise.notes
|
|
807
|
+
superset_id: exercise.supersetId ?? null,
|
|
808
|
+
notes: exercise.notes ?? null,
|
|
799
809
|
sets: exercise.sets.map((set) => ({
|
|
800
810
|
type: set.type,
|
|
801
|
-
weight_kg: set.weightKg
|
|
802
|
-
reps: set.reps
|
|
803
|
-
distance_meters: set.distanceMeters
|
|
804
|
-
duration_seconds: set.durationSeconds
|
|
805
|
-
rpe: set.rpe
|
|
806
|
-
custom_metric: set.customMetric
|
|
811
|
+
weight_kg: set.weight ?? set.weightKg ?? null,
|
|
812
|
+
reps: set.reps ?? null,
|
|
813
|
+
distance_meters: set.distance ?? set.distanceMeters ?? null,
|
|
814
|
+
duration_seconds: set.duration ?? set.durationSeconds ?? null,
|
|
815
|
+
rpe: set.rpe ?? null,
|
|
816
|
+
custom_metric: set.customMetric ?? null
|
|
807
817
|
}))
|
|
808
818
|
})
|
|
809
819
|
)
|
|
@@ -837,9 +847,12 @@ function registerWorkoutTools(server, hevyClient) {
|
|
|
837
847
|
sets: z5.array(
|
|
838
848
|
z5.object({
|
|
839
849
|
type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
|
|
850
|
+
weight: z5.coerce.number().optional().nullable(),
|
|
840
851
|
weightKg: z5.coerce.number().optional().nullable(),
|
|
841
852
|
reps: z5.coerce.number().int().optional().nullable(),
|
|
853
|
+
distance: z5.coerce.number().int().optional().nullable(),
|
|
842
854
|
distanceMeters: z5.coerce.number().int().optional().nullable(),
|
|
855
|
+
duration: z5.coerce.number().int().optional().nullable(),
|
|
843
856
|
durationSeconds: z5.coerce.number().int().optional().nullable(),
|
|
844
857
|
rpe: z5.coerce.number().optional().nullable(),
|
|
845
858
|
customMetric: z5.coerce.number().optional().nullable()
|
|
@@ -877,16 +890,16 @@ function registerWorkoutTools(server, hevyClient) {
|
|
|
877
890
|
exercises: exercises.map(
|
|
878
891
|
(exercise) => ({
|
|
879
892
|
exercise_template_id: exercise.exerciseTemplateId,
|
|
880
|
-
superset_id: exercise.supersetId
|
|
881
|
-
notes: exercise.notes
|
|
893
|
+
superset_id: exercise.supersetId ?? null,
|
|
894
|
+
notes: exercise.notes ?? null,
|
|
882
895
|
sets: exercise.sets.map((set) => ({
|
|
883
896
|
type: set.type,
|
|
884
|
-
weight_kg: set.weightKg
|
|
885
|
-
reps: set.reps
|
|
886
|
-
distance_meters: set.distanceMeters
|
|
887
|
-
duration_seconds: set.durationSeconds
|
|
888
|
-
rpe: set.rpe
|
|
889
|
-
custom_metric: set.customMetric
|
|
897
|
+
weight_kg: set.weight ?? set.weightKg ?? null,
|
|
898
|
+
reps: set.reps ?? null,
|
|
899
|
+
distance_meters: set.distance ?? set.distanceMeters ?? null,
|
|
900
|
+
duration_seconds: set.duration ?? set.durationSeconds ?? null,
|
|
901
|
+
rpe: set.rpe ?? null,
|
|
902
|
+
custom_metric: set.customMetric ?? null
|
|
890
903
|
}))
|
|
891
904
|
})
|
|
892
905
|
)
|
|
@@ -1220,15 +1233,23 @@ function createClient2(apiKey, baseUrl) {
|
|
|
1220
1233
|
|
|
1221
1234
|
// src/index.ts
|
|
1222
1235
|
dotenvx.config({ quiet: true });
|
|
1236
|
+
var sentryConfig = {
|
|
1237
|
+
dsn: "https://ce696d8333b507acbf5203eb877bce0f@o4508975499575296.ingest.de.sentry.io/4509049671647312",
|
|
1238
|
+
// Tracing must be enabled for MCP monitoring to work
|
|
1239
|
+
tracesSampleRate: 1,
|
|
1240
|
+
sendDefaultPii: false
|
|
1241
|
+
};
|
|
1242
|
+
Sentry.init(sentryConfig);
|
|
1223
1243
|
var HEVY_API_BASEURL = "https://api.hevyapp.com";
|
|
1224
1244
|
var serverConfigSchema = z6.object({
|
|
1225
1245
|
apiKey: z6.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).")
|
|
1226
1246
|
});
|
|
1227
1247
|
function buildServer(apiKey) {
|
|
1228
|
-
const
|
|
1248
|
+
const baseServer = new McpServer({
|
|
1229
1249
|
name,
|
|
1230
1250
|
version
|
|
1231
1251
|
});
|
|
1252
|
+
const server = Sentry.wrapMcpServerWithSentry(baseServer);
|
|
1232
1253
|
const hevyClient = createClient2(apiKey, HEVY_API_BASEURL);
|
|
1233
1254
|
console.error("Hevy client initialized with API key");
|
|
1234
1255
|
registerWorkoutTools(server, hevyClient);
|