@trainheroic-unofficial/athlete-mcp 3.4.0 → 3.5.1
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/server.mjs +89 -7
- package/package.json +3 -3
package/dist/server.mjs
CHANGED
|
@@ -271,6 +271,15 @@ const athleteWorkoutNoteObject = z.object({
|
|
|
271
271
|
rpe: z.number().int().min(1).max(10).optional()
|
|
272
272
|
});
|
|
273
273
|
const athleteWorkoutNoteArgsSchema = athleteWorkoutNoteObject.refine((v) => v.notes !== void 0 || v.rpe !== void 0, { message: "Provide notes and/or rpe" });
|
|
274
|
+
/**
|
|
275
|
+
* Args for the athlete per-exercise note write (the "Add exercise note" box on the exercise
|
|
276
|
+
* screen — band color, etc.). `savedWorkoutSetExerciseId` is the slot id from athlete_log_targets.
|
|
277
|
+
* Empty `notes` clears the note. Distinct from {@link athleteWorkoutNoteArgsSchema} (session note).
|
|
278
|
+
*/
|
|
279
|
+
const athleteExerciseNoteArgsSchema = z.object({
|
|
280
|
+
savedWorkoutSetExerciseId: idArgSchema,
|
|
281
|
+
notes: z.string()
|
|
282
|
+
});
|
|
274
283
|
const presentedNullNumber = z.number().nullable();
|
|
275
284
|
const presentedNullString = z.string().nullable();
|
|
276
285
|
/** Flattened exercise within a presented workout: prescriptions, logged results, units. */
|
|
@@ -278,6 +287,7 @@ const athleteWorkoutExerciseSchema = z.object({
|
|
|
278
287
|
exerciseId: presentedNullNumber,
|
|
279
288
|
title: z.string(),
|
|
280
289
|
instruction: presentedNullString,
|
|
290
|
+
notes: presentedNullString,
|
|
281
291
|
units: z.array(presentedNullString),
|
|
282
292
|
prescribed: z.array(z.string()),
|
|
283
293
|
performed: z.array(z.string())
|
|
@@ -387,6 +397,7 @@ z.array(rosterActivityRowSchema);
|
|
|
387
397
|
const presentedExerciseSessionSchema = z.object({
|
|
388
398
|
date: z.string(),
|
|
389
399
|
abr: presentedNullString,
|
|
400
|
+
notes: presentedNullString,
|
|
390
401
|
estimated1RM: presentedNullNumber,
|
|
391
402
|
sets: z.array(z.object({
|
|
392
403
|
setNumber: z.number(),
|
|
@@ -523,6 +534,7 @@ const logSetTargetOutputSchema = z.object({
|
|
|
523
534
|
savedWorkoutSetExerciseId: z.number(),
|
|
524
535
|
title: z.string(),
|
|
525
536
|
units: z.array(nullableString),
|
|
537
|
+
notes: nullableString,
|
|
526
538
|
prescribed: z.array(z.string()),
|
|
527
539
|
performed: z.array(z.string())
|
|
528
540
|
}))
|
|
@@ -597,6 +609,10 @@ const athleteWorkoutNoteOutputSchema = z.object({
|
|
|
597
609
|
notes: z.string(),
|
|
598
610
|
rpe: nullableNumber
|
|
599
611
|
});
|
|
612
|
+
const athleteExerciseNoteOutputSchema = z.object({
|
|
613
|
+
savedWorkoutSetExerciseId: z.number(),
|
|
614
|
+
notes: z.string()
|
|
615
|
+
});
|
|
600
616
|
const workoutReadExerciseOutputSchema = z.object({
|
|
601
617
|
order: z.number(),
|
|
602
618
|
title: z.string(),
|
|
@@ -699,6 +715,12 @@ const exerciseSpecSchema = z.object({
|
|
|
699
715
|
instr: z.string().optional(),
|
|
700
716
|
param_1_type: z.number().optional(),
|
|
701
717
|
param_2_type: z.number().optional()
|
|
718
|
+
}).superRefine((exercise, ctx) => {
|
|
719
|
+
if (Array.isArray(exercise.reps) && Array.isArray(exercise.weight) && exercise.reps.length !== exercise.weight.length) ctx.addIssue({
|
|
720
|
+
code: "custom",
|
|
721
|
+
message: `Per-set reps and weight arrays must have the same length; received ${exercise.reps.length} reps and ${exercise.weight.length} weights.`,
|
|
722
|
+
path: ["weight"]
|
|
723
|
+
});
|
|
702
724
|
});
|
|
703
725
|
/** A block's Red-Zone leaderboard: a unit string/number, or an object with options. */
|
|
704
726
|
const leaderboardSpecSchema = z.union([
|
|
@@ -1397,6 +1419,16 @@ function performedSlotsByExerciseId(sets) {
|
|
|
1397
1419
|
}
|
|
1398
1420
|
return map;
|
|
1399
1421
|
}
|
|
1422
|
+
/** Per-exercise athlete notes keyed by the prescription template id (`workout_set_exercise_id`). */
|
|
1423
|
+
function notesByTemplateId(sets) {
|
|
1424
|
+
const map = /* @__PURE__ */ new Map();
|
|
1425
|
+
for (const { exercises } of sets) for (const ex of exercises) {
|
|
1426
|
+
const id = coerceInt(ex.workout_set_exercise_id);
|
|
1427
|
+
const notes = str(ex.notes);
|
|
1428
|
+
if (id !== null && notes !== null) map.set(id, notes);
|
|
1429
|
+
}
|
|
1430
|
+
return map;
|
|
1431
|
+
}
|
|
1400
1432
|
/** Align an exercise's prescribed slots with the slots the athlete logged, per set index. */
|
|
1401
1433
|
function mergeExercise(prescribed, performed, meta) {
|
|
1402
1434
|
const indices = [.../* @__PURE__ */ new Set([...prescribed.keys(), ...performed.keys()])].sort((a, b) => a - b);
|
|
@@ -1410,7 +1442,7 @@ function mergeExercise(prescribed, performed, meta) {
|
|
|
1410
1442
|
};
|
|
1411
1443
|
}
|
|
1412
1444
|
/** A prescription block, each exercise aligned with the slots the athlete logged against it. */
|
|
1413
|
-
function mergePrescriptionBlock(set, performedById) {
|
|
1445
|
+
function mergePrescriptionBlock(set, performedById, notesById) {
|
|
1414
1446
|
const exercises = Array.isArray(set.workoutSetExercises) ? set.workoutSetExercises : [];
|
|
1415
1447
|
return {
|
|
1416
1448
|
order: coerceInt(set.order) ?? 0,
|
|
@@ -1424,6 +1456,7 @@ function mergePrescriptionBlock(set, performedById) {
|
|
|
1424
1456
|
exerciseId: coerceInt(ex.exercise_id),
|
|
1425
1457
|
title: typeof ex.title === "string" ? ex.title : "",
|
|
1426
1458
|
instruction: str(ex.instruction),
|
|
1459
|
+
notes: id !== null ? notesById.get(id) ?? null : null,
|
|
1427
1460
|
units: exerciseUnits(ex.param_1_type, ex.param_2_type)
|
|
1428
1461
|
});
|
|
1429
1462
|
})
|
|
@@ -1440,6 +1473,7 @@ function mergeSavedBlock(set, exercises) {
|
|
|
1440
1473
|
exerciseId: coerceInt(ex.exercise_id),
|
|
1441
1474
|
title: typeof ex.exercise_title === "string" ? ex.exercise_title : "",
|
|
1442
1475
|
instruction: str(ex.instruction),
|
|
1476
|
+
notes: str(ex.notes),
|
|
1443
1477
|
units: exerciseUnits(ex.param_1_type, ex.param_2_type)
|
|
1444
1478
|
}))
|
|
1445
1479
|
};
|
|
@@ -1458,7 +1492,8 @@ function mergeAthleteWorkout(raw) {
|
|
|
1458
1492
|
const prescriptionSets = (Array.isArray(workout.workoutSets) ? workout.workoutSets : []).filter(isRecord);
|
|
1459
1493
|
const logged = savedSets(saved);
|
|
1460
1494
|
const performedById = performedSlotsByExerciseId(logged);
|
|
1461
|
-
const
|
|
1495
|
+
const notesById = notesByTemplateId(logged);
|
|
1496
|
+
const blocks = prescriptionSets.map((s) => mergePrescriptionBlock(s, performedById, notesById)).sort((a, b) => a.order - b.order);
|
|
1462
1497
|
const prescribedIds = /* @__PURE__ */ new Set();
|
|
1463
1498
|
for (const s of prescriptionSets) {
|
|
1464
1499
|
const exs = Array.isArray(s.workoutSetExercises) ? s.workoutSetExercises : [];
|
|
@@ -1504,6 +1539,7 @@ function toStringExercise(ex) {
|
|
|
1504
1539
|
exerciseId: ex.exerciseId,
|
|
1505
1540
|
title: ex.title,
|
|
1506
1541
|
instruction: ex.instruction,
|
|
1542
|
+
notes: ex.notes,
|
|
1507
1543
|
units: ex.units,
|
|
1508
1544
|
prescribed: sideStrings(ex.sets, (s) => s.prescribed),
|
|
1509
1545
|
performed: sideStrings(ex.sets, (s) => s.performed)
|
|
@@ -1598,6 +1634,7 @@ function presentLogTargets(list) {
|
|
|
1598
1634
|
savedWorkoutSetExerciseId: id,
|
|
1599
1635
|
title: exerciseTitle(ex),
|
|
1600
1636
|
units: exerciseUnits(ex.param_1_type, ex.param_2_type),
|
|
1637
|
+
notes: str(ex.notes),
|
|
1601
1638
|
prescribed: prescribedStrings(ex),
|
|
1602
1639
|
performed: performedStrings(ex)
|
|
1603
1640
|
};
|
|
@@ -1665,6 +1702,7 @@ function presentExerciseHistory(detail) {
|
|
|
1665
1702
|
sessions: (detail.history ?? []).map((h) => ({
|
|
1666
1703
|
date: h.dateCompleted,
|
|
1667
1704
|
abr: h.abr ?? null,
|
|
1705
|
+
notes: str(h.notes),
|
|
1668
1706
|
estimated1RM: h.bestEstimated1RM ?? null,
|
|
1669
1707
|
sets: (h.sets ?? []).map((s) => ({
|
|
1670
1708
|
setNumber: s.setNumber,
|
|
@@ -2253,6 +2291,28 @@ async function setAthleteWorkoutNote(client, args) {
|
|
|
2253
2291
|
};
|
|
2254
2292
|
}
|
|
2255
2293
|
//#endregion
|
|
2294
|
+
//#region ../js/src/athlete-exercise-note.ts
|
|
2295
|
+
/**
|
|
2296
|
+
* PUT /1.0/athlete/savedworkoutsetexercise/{id} — set the athlete's per-exercise note (the
|
|
2297
|
+
* "Add exercise note" box on the exercise screen). A notes-only body leaves logged reps/weight
|
|
2298
|
+
* untouched. Empty `notes` clears the note. GET on that path 405s; the range read and exercise
|
|
2299
|
+
* history both echo the stored string. Coach-visible.
|
|
2300
|
+
*/
|
|
2301
|
+
async function setAthleteExerciseNote(client, args) {
|
|
2302
|
+
const savedWorkoutSetExerciseId = coerceInt(args.savedWorkoutSetExerciseId);
|
|
2303
|
+
if (savedWorkoutSetExerciseId === null) throw new Error(`Invalid savedWorkoutSetExerciseId ${String(args.savedWorkoutSetExerciseId)}.`);
|
|
2304
|
+
const res = await client.request("PUT", `/1.0/athlete/savedworkoutsetexercise/${savedWorkoutSetExerciseId}`, { body: {
|
|
2305
|
+
id: savedWorkoutSetExerciseId,
|
|
2306
|
+
notes: args.notes
|
|
2307
|
+
} });
|
|
2308
|
+
if (!res.ok) throw new Error(`Set exercise note failed (HTTP ${res.status}).`);
|
|
2309
|
+
const data = isRecord(res.data) ? res.data : {};
|
|
2310
|
+
return {
|
|
2311
|
+
savedWorkoutSetExerciseId,
|
|
2312
|
+
notes: typeof data.notes === "string" ? data.notes : args.notes
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
//#endregion
|
|
2256
2316
|
//#region ../core/src/history.ts
|
|
2257
2317
|
/**
|
|
2258
2318
|
* Trim a presented exercise history's session time-series to an inclusive YYYY-MM-DD window.
|
|
@@ -2332,9 +2392,9 @@ function registerProfileTools(server, ctx, whoami, userId) {
|
|
|
2332
2392
|
return jsonResult(await fetchLeaderboard(ctx.client, toId(workoutId), opts));
|
|
2333
2393
|
}));
|
|
2334
2394
|
}
|
|
2335
|
-
const ATHLETE_WORKOUTS_DESC = "Workouts on the AUTHENTICATED user's own athlete calendar in an inclusive YYYY-MM-DD window, flattened to blocks/exercises. When a coach account calls this, it returns the coach's own training schedule — not a roster athlete's. To inspect a roster athlete use athlete_saved_workouts (or athlete_training for a month overview); to verify a coach-published team session from the calendar side use workout_read. Each exercise carries both its `prescribed` sets (what the program called for) and `performed` sets (what the athlete actually logged); each workout has a top-level `logged` flag. Session `notes` and `rpe` are the athlete's own note/RPE on that workout (null when unset), distinct from coach `instruction`. Use `performed`/`logged` to tell what was recorded or done. That is the reliable signal: a session can hold logged sets while the API's own completion flags stay 0, and an empty `performed` means nothing was logged for that exercise. For 'did I record anything / what did I do', set loggedOnly:true to return only sessions with logged sets (it keeps whole sessions that have any logged set, so individual exercises inside can still show empty `performed`; it also shrinks a large result); limit returns the most recent N workouts (newest first). For a high-level overview ('what's on my schedule this week', 'what have I been training lately') set summary:true to get one compact row per session (date, program, title, logged flag, and exerciseCount/performedCount — read performedCount against exerciseCount, e.g. 1 of 12 logged) instead of every set — a multi-program week of full detail is large, so prefer summary first, then re-query a single day without it to drill into that day's sets. For 'what's my next workout', query a forward window from today; if it comes back empty, the most recent session is likely yesterday's still-unlogged one, so widen the window backward a day or two. Both filters apply to the presented view, not raw. raw:true returns the untouched API objects. This is a date-windowed fetch, NOT an aggregate: for lifetime totals (all-time session count, total volume, first/last logged date) call athlete_profile instead of summing windows — a multi-year range here can time out. Narrow the window if the result is truncated.";
|
|
2336
|
-
const ATHLETE_LOG_TARGETS_DESC = "The savedWorkoutSetId + savedWorkoutSetExerciseId that athlete_log_set needs, read straight off your scheduled/logged workouts in an inclusive YYYY-MM-DD window — no raw needed. This is the self-service path for logging into a COACH-SCHEDULED workout: read the ids here, then pass them to athlete_log_set. The default view is COMPACT: one row per saved set, each carrying its program/programId, the savedWorkoutSetId, and every exercise's savedWorkoutSetExerciseId with prescribed/performed values
|
|
2337
|
-
const ATHLETE_EXERCISE_HISTORY_DESC = "Per-exercise PRs and the dated session time-series (sets performed, estimated 1RM). The returned `sessions` run all-time, newest first; pass since/until (YYYY-MM-DD, inclusive) to keep only sessions in that window — use it for 'last 3 months' / 'this year' questions so the result stays small. estimated1RM is a formula off the session's best set and reads high when a session mixes a heavy single with high-rep backdown work, so treat it as approximate, not a logged single. `liftPRs` are always all-time and ignore since/until, but each carries the date it was set, so filter those dates yourself to answer 'PRs set this year'. Set raw:true for the untouched API object. Get the exercise id from athlete_exercises.";
|
|
2395
|
+
const ATHLETE_WORKOUTS_DESC = "Workouts on the AUTHENTICATED user's own athlete calendar in an inclusive YYYY-MM-DD window, flattened to blocks/exercises. When a coach account calls this, it returns the coach's own training schedule — not a roster athlete's. To inspect a roster athlete use athlete_saved_workouts (or athlete_training for a month overview); to verify a coach-published team session from the calendar side use workout_read. Each exercise carries both its `prescribed` sets (what the program called for) and `performed` sets (what the athlete actually logged); each workout has a top-level `logged` flag. Session `notes` and `rpe` are the athlete's own note/RPE on that workout (null when unset), distinct from coach `instruction`. Each exercise may also carry `notes` (the per-exercise box on the exercise screen; null when unset). Use `performed`/`logged` to tell what was recorded or done. That is the reliable signal: a session can hold logged sets while the API's own completion flags stay 0, and an empty `performed` means nothing was logged for that exercise. For 'did I record anything / what did I do', set loggedOnly:true to return only sessions with logged sets (it keeps whole sessions that have any logged set, so individual exercises inside can still show empty `performed`; it also shrinks a large result); limit returns the most recent N workouts (newest first). For a high-level overview ('what's on my schedule this week', 'what have I been training lately') set summary:true to get one compact row per session (date, program, title, logged flag, and exerciseCount/performedCount — read performedCount against exerciseCount, e.g. 1 of 12 logged) instead of every set — a multi-program week of full detail is large, so prefer summary first, then re-query a single day without it to drill into that day's sets. For 'what's my next workout', query a forward window from today; if it comes back empty, the most recent session is likely yesterday's still-unlogged one, so widen the window backward a day or two. Both filters apply to the presented view, not raw. raw:true returns the untouched API objects. This is a date-windowed fetch, NOT an aggregate: for lifetime totals (all-time session count, total volume, first/last logged date) call athlete_profile instead of summing windows — a multi-year range here can time out. Narrow the window if the result is truncated.";
|
|
2396
|
+
const ATHLETE_LOG_TARGETS_DESC = "The savedWorkoutSetId + savedWorkoutSetExerciseId that athlete_log_set needs, read straight off your scheduled/logged workouts in an inclusive YYYY-MM-DD window — no raw needed. This is the self-service path for logging into a COACH-SCHEDULED workout: read the ids here, then pass them to athlete_log_set. The default view is COMPACT: one row per saved set, each carrying its program/programId, the savedWorkoutSetId, and every exercise's savedWorkoutSetExerciseId with prescribed/performed values and any per-exercise `notes`. When several workouts fall on the same day (you're on more than one program), narrow to one with program (a case-insensitive title substring, e.g. 'bodybuilding' — no id lookup needed), or programId/teamId if you have the id. raw:true returns the untouched API objects, but that blob is large and can be truncated when several workouts share a date — prefer the program filter + the default view. For reading what you actually did (not the log ids), use athlete_workouts.";
|
|
2397
|
+
const ATHLETE_EXERCISE_HISTORY_DESC = "Per-exercise PRs and the dated session time-series (sets performed, estimated 1RM). The returned `sessions` run all-time, newest first; pass since/until (YYYY-MM-DD, inclusive) to keep only sessions in that window — use it for 'last 3 months' / 'this year' questions so the result stays small. estimated1RM is a formula off the session's best set and reads high when a session mixes a heavy single with high-rep backdown work, so treat it as approximate, not a logged single. Each session may carry `notes` (the per-exercise athlete note). `liftPRs` are always all-time and ignore since/until, but each carries the date it was set, so filter those dates yourself to answer 'PRs set this year'. Set raw:true for the untouched API object. Get the exercise id from athlete_exercises.";
|
|
2338
2398
|
const ATHLETE_EXERCISES_DESC = "The exercises the athlete has logged (id + title + positional units). Call with no arguments to get the athlete's complete catalog, which often runs to several hundred entries; leave limit off when you want the whole list, since a no-q call returns everything by default. Pass q to free-text search by name (returns the top ranked matches, capped by limit, default 20). Use a returned id with athlete_exercise_history, athlete_personal_records, or athlete_exercise_stats (all of which require an exercise id). If a no-q result comes back wrapped with a __truncated marker, you passed a limit smaller than the catalog and are seeing a partial list; re-call without limit for all of it. A common name returns several variants (e.g. plain 'Bench Press' id 1162 vs 'BARBELL BENCH PRESS'); each variant keeps its own separate history and PR board, so prefer the plain canonical entry, and if a PR or trend looks incomplete, the work may be split across variants, so check the others.";
|
|
2339
2399
|
function runAthleteWorkouts(ctx, args) {
|
|
2340
2400
|
return attempt(async () => {
|
|
@@ -2507,7 +2567,7 @@ function registerSessionTools(server, ctx) {
|
|
|
2507
2567
|
function registerWorkoutNoteTool(server, ctx) {
|
|
2508
2568
|
server.registerTool("athlete_workout_note", {
|
|
2509
2569
|
title: "Set your session note or RPE",
|
|
2510
|
-
description: "Athlete-facing write: set the free-text note (and/or session RPE 1–10) on a workout. This is the note box on the workout screen, not the coach's Instructions. programWorkoutId is the `id` from athlete_workouts for that date. Pass notes (empty string clears) and/or rpe; a notes-only write leaves rpe untouched and vice versa. Visible to your coach. Requires confirmation (elicitation or confirm:true).",
|
|
2570
|
+
description: "Athlete-facing write: set the free-text note (and/or session RPE 1–10) on a workout. This is the note box on the workout screen, not the coach's Instructions and not the per-exercise note (use athlete_exercise_note for that). programWorkoutId is the `id` from athlete_workouts for that date. Pass notes (empty string clears) and/or rpe; a notes-only write leaves rpe untouched and vice versa. Visible to your coach. Requires confirmation (elicitation or confirm:true).",
|
|
2511
2571
|
inputSchema: {
|
|
2512
2572
|
...athleteWorkoutNoteObject.shape,
|
|
2513
2573
|
confirm: z.boolean().optional()
|
|
@@ -2526,6 +2586,27 @@ function registerWorkoutNoteTool(server, ctx) {
|
|
|
2526
2586
|
return jsonResult(await setAthleteWorkoutNote(ctx.client, patch));
|
|
2527
2587
|
}));
|
|
2528
2588
|
}
|
|
2589
|
+
/** Set the athlete's per-exercise note on a saved slot (coach-visible). */
|
|
2590
|
+
function registerExerciseNoteTool(server, ctx) {
|
|
2591
|
+
server.registerTool("athlete_exercise_note", {
|
|
2592
|
+
title: "Set a per-exercise note",
|
|
2593
|
+
description: "Athlete-facing write: set the free-text note on one exercise in a workout (the 'Add exercise note' box on the exercise screen). Use this for things the weight field cannot hold, such as which band you used. Distinct from athlete_workout_note, which writes the session-level note. savedWorkoutSetExerciseId is the slot id from athlete_log_targets. Empty notes clears the note. Visible to your coach. Requires confirmation (elicitation or confirm:true).",
|
|
2594
|
+
inputSchema: {
|
|
2595
|
+
...athleteExerciseNoteArgsSchema.shape,
|
|
2596
|
+
confirm: z.boolean().optional()
|
|
2597
|
+
},
|
|
2598
|
+
outputSchema: toolOutputSchema(athleteExerciseNoteOutputSchema),
|
|
2599
|
+
annotations: DESTRUCTIVE
|
|
2600
|
+
}, ({ savedWorkoutSetExerciseId, notes, confirm }, extra) => attempt(async () => {
|
|
2601
|
+
const patch = athleteExerciseNoteArgsSchema.parse({
|
|
2602
|
+
savedWorkoutSetExerciseId,
|
|
2603
|
+
notes
|
|
2604
|
+
});
|
|
2605
|
+
const blocked = confirmGate(extra, `Set the exercise note on slot ${toId(patch.savedWorkoutSetExerciseId)}? This is visible to your coach.`, confirm);
|
|
2606
|
+
if (blocked) return blocked;
|
|
2607
|
+
return jsonResult(await setAthleteExerciseNote(ctx.client, patch));
|
|
2608
|
+
}));
|
|
2609
|
+
}
|
|
2529
2610
|
/** Map validated logSession exercises to the SDK's SessionExercise[] (ids coerced, slots trimmed). */
|
|
2530
2611
|
function mapSessionExercises(exercises) {
|
|
2531
2612
|
return exercises.map((e) => {
|
|
@@ -2678,12 +2759,13 @@ function registerAthleteTrainingTools(server, ctx) {
|
|
|
2678
2759
|
registerLogTargetsTool(server, ctx);
|
|
2679
2760
|
registerSessionTools(server, ctx);
|
|
2680
2761
|
registerWorkoutNoteTool(server, ctx);
|
|
2762
|
+
registerExerciseNoteTool(server, ctx);
|
|
2681
2763
|
registerLogTool(server, ctx);
|
|
2682
2764
|
registerSwapTool(server, ctx);
|
|
2683
2765
|
}
|
|
2684
2766
|
//#endregion
|
|
2685
2767
|
//#region package.json
|
|
2686
|
-
var version = "3.
|
|
2768
|
+
var version = "3.5.1";
|
|
2687
2769
|
//#endregion
|
|
2688
2770
|
//#region src/server.ts
|
|
2689
2771
|
function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainheroic-unofficial/athlete-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@modelcontextprotocol/server": "2.0.0",
|
|
23
23
|
"zod": "^4.4.3",
|
|
24
|
-
"@trainheroic-unofficial/core": "3.
|
|
25
|
-
"@trainheroic-unofficial/js": "3.
|
|
24
|
+
"@trainheroic-unofficial/core": "3.5.1",
|
|
25
|
+
"@trainheroic-unofficial/js": "3.5.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^26.3.0",
|