@trainheroic-unofficial/athlete-mcp 3.0.1 → 3.1.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 +6 -3
- package/dist/server.mjs +46 -6
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Local single-user [MCP](https://modelcontextprotocol.io) server for a TrainHeroi
|
|
|
4
4
|
|
|
5
5
|
It reads two required environment variables, `TRAINHEROIC_EMAIL` and `TRAINHEROIC_PASSWORD` (your existing TrainHeroic login). These are real credentials in plaintext; the config below puts them in a file or shell history, so treat them as secrets.
|
|
6
6
|
|
|
7
|
-
It exposes the logged-in athlete's own training: scheduled and completed workouts, per-exercise history, PRs (personal records), working maxes,
|
|
7
|
+
It exposes the logged-in athlete's own training: scheduled and completed workouts, per-exercise history, PRs (personal records), working maxes, lifetime totals, and confirmation-gated training writes. Coaching capabilities (rosters, teams, programs, messaging) are available through [`@trainheroic-unofficial/coach-mcp`](../coach-mcp). A hosted version that holds credentials server-side behind OAuth and gives a coach login both the athlete and coaching tools is described in the [root README](../../README.md).
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -48,8 +48,8 @@ A coach account works here too: a TrainHeroic coach account also has its own ath
|
|
|
48
48
|
|
|
49
49
|
## Tools
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
The assistant fills in each tool's parameters (dates are `YYYY-MM-DD`); your MCP client shows the
|
|
52
|
+
full schema for each. Writes are confirmation-gated.
|
|
53
53
|
|
|
54
54
|
- `athlete_whoami`: identity (id, name, roles)
|
|
55
55
|
- `athlete_profile`: lifetime totals + profile
|
|
@@ -67,6 +67,9 @@ are `YYYY-MM-DD`); your MCP client shows the full schema for each.
|
|
|
67
67
|
- `athlete_log_set`: logs completed set results to your training log. This is a real write
|
|
68
68
|
that your coach can see. It confirms before running: the server asks the client to confirm,
|
|
69
69
|
falling back to an explicit `confirm: true` argument when the client can't prompt.
|
|
70
|
+
- `athlete_prescribe_set`: sets your planned reps and/or weight without recording the set as
|
|
71
|
+
completed. It replaces the selected exercise's whole prescription, so every planned set and
|
|
72
|
+
value to keep must be included.
|
|
70
73
|
|
|
71
74
|
---
|
|
72
75
|
|
package/dist/server.mjs
CHANGED
|
@@ -203,12 +203,18 @@ const logSetArgsSchema = z.object({
|
|
|
203
203
|
results: loggedExerciseResultsSchema
|
|
204
204
|
});
|
|
205
205
|
logSetArgsSchema.extend({ athleteId: idArgSchema });
|
|
206
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Args for changing the logged-in athlete's prescribed targets without marking the set performed.
|
|
208
|
+
* The write replaces the exercise's whole prescription, so its sets are positional and sequential
|
|
209
|
+
* by definition: it deliberately omits the log path's `slot` field because a sparse prescription
|
|
210
|
+
* has no meaning here.
|
|
211
|
+
*/
|
|
212
|
+
const athletePrescribeSetArgsSchema = z.object({
|
|
207
213
|
date: dateString,
|
|
208
214
|
savedWorkoutSetId: idArgSchema,
|
|
209
|
-
athleteId: idArgSchema,
|
|
210
215
|
results: prescribedExerciseResultsSchema
|
|
211
216
|
});
|
|
217
|
+
athletePrescribeSetArgsSchema.extend({ athleteId: idArgSchema });
|
|
212
218
|
/**
|
|
213
219
|
* Args for the coach per-athlete exercise swap: replace the exercise prescribed in one of a
|
|
214
220
|
* roster athlete's saved-workout slots with a different exercise, the API equivalent of the
|
|
@@ -1427,6 +1433,21 @@ async function logAthleteSet(client, args) {
|
|
|
1427
1433
|
};
|
|
1428
1434
|
}
|
|
1429
1435
|
/**
|
|
1436
|
+
* Set the logged-in athlete's prescribed reps/weight for one saved workout set WITHOUT marking it
|
|
1437
|
+
* performed. This is the athlete-side equivalent of editing target values in the app: it writes
|
|
1438
|
+
* `param_N_data_M` with every made/completed flag cleared and skips the set-completion PUT.
|
|
1439
|
+
*
|
|
1440
|
+
* The write replaces the selected exercise's whole prescription, so callers must pass every set
|
|
1441
|
+
* they want to keep. An omitted param is cleared rather than preserved.
|
|
1442
|
+
*/
|
|
1443
|
+
async function prescribeAthleteSet(client, args) {
|
|
1444
|
+
const r = await writeSetResults(client, { role: "athlete" }, await fetchAthleteWorkouts(client, args.date, args.date), args.savedWorkoutSetId, args.results, "prescribe");
|
|
1445
|
+
return {
|
|
1446
|
+
savedWorkoutSetId: r.savedWorkoutSetId,
|
|
1447
|
+
exercisesPrescribed: r.exercisesWritten
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1430
1451
|
* Swap the exercise prescribed in one of an athlete's saved-workout slots for a different
|
|
1431
1452
|
* exercise — the API equivalent of the app's per-athlete "swap exercise":
|
|
1432
1453
|
* `PUT /v5/savedWorkoutSetExercises/{savedWorkoutSetExerciseId}?exerciseId={exerciseId}` with an
|
|
@@ -1462,9 +1483,10 @@ async function swapAthleteExercise(client, args) {
|
|
|
1462
1483
|
};
|
|
1463
1484
|
}
|
|
1464
1485
|
/**
|
|
1465
|
-
* Shared set-write behind {@link logAthleteSet}, {@link
|
|
1466
|
-
* {@link prescribeForAthlete}. `target` selects the surface: `athlete`
|
|
1467
|
-
* `coach` writes `/1.0/coach/...{athleteId}` and stamps `athleteId` into
|
|
1486
|
+
* Shared set-write behind {@link logAthleteSet}, {@link prescribeAthleteSet},
|
|
1487
|
+
* {@link logForAthlete}, and {@link prescribeForAthlete}. `target` selects the surface: `athlete`
|
|
1488
|
+
* writes `/1.0/athlete/...`; `coach` writes `/1.0/coach/...{athleteId}` and stamps `athleteId` into
|
|
1489
|
+
* each body.
|
|
1468
1490
|
*
|
|
1469
1491
|
* Step 1 PUTs each exercise's per-set values to its own endpoint (the only path that actually
|
|
1470
1492
|
* stores reps and weight). `mode` decides what those values mean: `"log"` records them as a
|
|
@@ -2047,6 +2069,24 @@ function registerLogTool(server, ctx) {
|
|
|
2047
2069
|
results: toSetResults(results)
|
|
2048
2070
|
}));
|
|
2049
2071
|
}));
|
|
2072
|
+
server.registerTool("athlete_prescribe_set", {
|
|
2073
|
+
title: "Set your prescribed reps/weight without logging",
|
|
2074
|
+
description: "Athlete-facing write: set the planned reps and/or weight for one of your scheduled workout sets WITHOUT recording it as performed. Use this when you know the target load before training but have not completed the set yet. The set stays open and nothing is added to exercise history. param1 is reps and param2 is weight; pass one sets[] entry per planned set. This write REPLACES the selected exercise's whole prescription, so include every planned set and every value you want to keep; an omitted param is cleared. Get savedWorkoutSetId + savedWorkoutSetExerciseId from athlete_log_targets. To record actual completed results, use athlete_log_set instead. Requires confirmation (elicitation or confirm:true).",
|
|
2075
|
+
inputSchema: {
|
|
2076
|
+
...athletePrescribeSetArgsSchema.shape,
|
|
2077
|
+
confirm: z.boolean().optional()
|
|
2078
|
+
},
|
|
2079
|
+
annotations: DESTRUCTIVE
|
|
2080
|
+
}, ({ date, savedWorkoutSetId, results, confirm }, extra) => attempt(async () => {
|
|
2081
|
+
const id = toId(savedWorkoutSetId);
|
|
2082
|
+
const blocked = confirmGate(extra, `Set prescribed values on your saved workout set ${id} on ${date}? This changes your planned reps/weight but does not log the set as completed.`, confirm);
|
|
2083
|
+
if (blocked) return blocked;
|
|
2084
|
+
return jsonResult(await prescribeAthleteSet(ctx.client, {
|
|
2085
|
+
date,
|
|
2086
|
+
savedWorkoutSetId: id,
|
|
2087
|
+
results: toSetResults(results)
|
|
2088
|
+
}));
|
|
2089
|
+
}));
|
|
2050
2090
|
}
|
|
2051
2091
|
/**
|
|
2052
2092
|
* The athlete's own per-exercise swap, in its own function so registerLogTool/registerSessionTools
|
|
@@ -2103,7 +2143,7 @@ function registerAthleteTrainingTools(server, ctx) {
|
|
|
2103
2143
|
}
|
|
2104
2144
|
//#endregion
|
|
2105
2145
|
//#region package.json
|
|
2106
|
-
var version = "3.0
|
|
2146
|
+
var version = "3.1.0";
|
|
2107
2147
|
//#endregion
|
|
2108
2148
|
//#region src/server.ts
|
|
2109
2149
|
function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainheroic-unofficial/athlete-mcp",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
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.0
|
|
25
|
-
"@trainheroic-unofficial/js": "3.0
|
|
24
|
+
"@trainheroic-unofficial/core": "3.1.0",
|
|
25
|
+
"@trainheroic-unofficial/js": "3.1.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^26.2.0",
|