@trainheroic-unofficial/athlete-mcp 1.2.0 → 1.4.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/dist/server.mjs +33 -7
- package/package.json +3 -3
package/dist/server.mjs
CHANGED
|
@@ -453,7 +453,20 @@ async function confirmGate(server, requestId, message, confirmArg) {
|
|
|
453
453
|
const SERVER_INSTRUCTIONS = "Speak to the user in plain, everyday language about their training. Describe what you are doing in the TrainHeroic app's own terms (for example, say you are creating a workout rather than naming a tool). Do not surface internal tool names (the snake_case identifiers such as athlete_session_create), raw parameter names, or numeric ids in your replies unless the user explicitly asks for them; they are implementation details. The tool descriptions cross-reference each other by name only so you can chain them correctly. Keep that wiring to yourself.";
|
|
454
454
|
//#endregion
|
|
455
455
|
//#region ../js/src/auth.ts
|
|
456
|
-
const
|
|
456
|
+
const DEFAULT_AUTH_URL = "https://apis.trainheroic.com/auth";
|
|
457
|
+
/**
|
|
458
|
+
* The login endpoint, allowing an env override so a test harness can authenticate against a local
|
|
459
|
+
* fake backend. Precedence: an explicit `TH_AUTH_URL`, else `${TH_APIS_BASE}/auth` (login lives on
|
|
460
|
+
* the apis host, so it follows that base), else the real endpoint. Read via `globalThis.process`
|
|
461
|
+
* (no `import process`) to keep this module workerd-safe, and per call so a child-env override wins.
|
|
462
|
+
*/
|
|
463
|
+
function authUrl() {
|
|
464
|
+
const env = globalThis.process?.env;
|
|
465
|
+
if (env?.TH_AUTH_URL && env.TH_AUTH_URL.length > 0) return env.TH_AUTH_URL;
|
|
466
|
+
const apis = env?.TH_APIS_BASE;
|
|
467
|
+
if (apis && apis.length > 0) return `${apis.replace(/\/$/, "")}/auth`;
|
|
468
|
+
return DEFAULT_AUTH_URL;
|
|
469
|
+
}
|
|
457
470
|
/**
|
|
458
471
|
* Authenticate against TrainHeroic. Returns the session bundle, or null on bad
|
|
459
472
|
* credentials. TrainHeroic returns only { id, scope, role, session_id } (verified in
|
|
@@ -461,7 +474,7 @@ const AUTH_URL = "https://apis.trainheroic.com/auth";
|
|
|
461
474
|
* is sent as the `session-token` header and works against both API hosts.
|
|
462
475
|
*/
|
|
463
476
|
async function loginTrainHeroic(email, password) {
|
|
464
|
-
const res = await fetch(
|
|
477
|
+
const res = await fetch(authUrl(), {
|
|
465
478
|
method: "POST",
|
|
466
479
|
headers: {
|
|
467
480
|
"content-type": "application/x-www-form-urlencoded",
|
|
@@ -484,8 +497,19 @@ async function loginTrainHeroic(email, password) {
|
|
|
484
497
|
}
|
|
485
498
|
//#endregion
|
|
486
499
|
//#region ../js/src/client.ts
|
|
487
|
-
const
|
|
488
|
-
const
|
|
500
|
+
const DEFAULT_COACH_BASE = "https://api.trainheroic.com";
|
|
501
|
+
const DEFAULT_APIS_BASE = "https://apis.trainheroic.com";
|
|
502
|
+
/**
|
|
503
|
+
* Resolve an API host, allowing an env override. The override exists so a test harness can point
|
|
504
|
+
* the client at a local fake backend (and it doubles as a staging knob); production leaves these
|
|
505
|
+
* unset and gets the real hosts. Read through `globalThis.process?.env` — not an `import process`
|
|
506
|
+
* — so the runtime-agnostic `.` entry stays free of `node:*` and runs unchanged on workerd, and
|
|
507
|
+
* read per request (not at module load) so a value the harness sets in the child env always wins.
|
|
508
|
+
*/
|
|
509
|
+
function envBase(key, fallback) {
|
|
510
|
+
const v = (globalThis.process?.env)?.[key];
|
|
511
|
+
return v && v.length > 0 ? v : fallback;
|
|
512
|
+
}
|
|
489
513
|
var TrainHeroicAuthError = class extends Error {
|
|
490
514
|
name = "TrainHeroicAuthError";
|
|
491
515
|
};
|
|
@@ -524,7 +548,7 @@ var TrainHeroicClient = class {
|
|
|
524
548
|
return this.#sessionId;
|
|
525
549
|
}
|
|
526
550
|
async request(method, path, options = {}) {
|
|
527
|
-
const url = `${options.base === "apis" ?
|
|
551
|
+
const url = `${options.base === "apis" ? envBase("TH_APIS_BASE", DEFAULT_APIS_BASE) : envBase("TH_COACH_BASE", DEFAULT_COACH_BASE)}/${path.replace(/^\//, "")}`;
|
|
528
552
|
let session = await this.#ensureSession();
|
|
529
553
|
let res = await this.#send(method, url, session, options.body);
|
|
530
554
|
if (res.status === 401 || res.status === 403) {
|
|
@@ -1082,7 +1106,7 @@ async function writeSetResults(client, target, workouts, savedWorkoutSetId, resu
|
|
|
1082
1106
|
throw new Error(`savedWorkoutSetExerciseId ${result.savedWorkoutSetExerciseId} not found in saved workout set ${savedWorkoutSetId}. Exercises in this set: ${valid.join(", ") || "none"}.`);
|
|
1083
1107
|
}
|
|
1084
1108
|
const workoutSetExerciseId = coerceInt(ex.workout_set_exercise_id);
|
|
1085
|
-
if (!workoutSetExerciseId) throw new Error(`
|
|
1109
|
+
if (!workoutSetExerciseId) throw new Error(`savedWorkoutSetExercise ${result.savedWorkoutSetExerciseId} is missing its workout_set_exercise_id (the prescription-template pointer the write needs). This is the savedWorkoutSetExerciseId, not an exercise_id — re-read the ids from athlete_saved_workouts.`);
|
|
1086
1110
|
const body = {
|
|
1087
1111
|
...buildExerciseSetPayload(result.savedWorkoutSetExerciseId, savedWorkoutSetId, workoutSetExerciseId, result.sets, mode),
|
|
1088
1112
|
...extra
|
|
@@ -1261,6 +1285,7 @@ function presentExerciseHistory(detail) {
|
|
|
1261
1285
|
description: p.description ?? null,
|
|
1262
1286
|
reps: p.reps ?? null,
|
|
1263
1287
|
weight: p.weight ?? null,
|
|
1288
|
+
units: p.units ?? null,
|
|
1264
1289
|
date: p.dateCompleted ?? null
|
|
1265
1290
|
})),
|
|
1266
1291
|
sessions: (detail.history ?? []).map((h) => ({
|
|
@@ -1295,6 +1320,7 @@ function historyInRange(presented, since, until) {
|
|
|
1295
1320
|
sessions
|
|
1296
1321
|
};
|
|
1297
1322
|
}
|
|
1323
|
+
z.number().int().positive().max(36).optional();
|
|
1298
1324
|
//#endregion
|
|
1299
1325
|
//#region ../core/src/tools/athlete-training.ts
|
|
1300
1326
|
/** Identity, profile, prefs, working maxes, leaderboard. */
|
|
@@ -1548,7 +1574,7 @@ function registerAthleteTrainingTools(server, ctx) {
|
|
|
1548
1574
|
}
|
|
1549
1575
|
//#endregion
|
|
1550
1576
|
//#region package.json
|
|
1551
|
-
var version = "1.
|
|
1577
|
+
var version = "1.4.0";
|
|
1552
1578
|
//#endregion
|
|
1553
1579
|
//#region src/server.ts
|
|
1554
1580
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainheroic-unofficial/athlete-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
23
23
|
"zod": "^4.4.3",
|
|
24
|
-
"@trainheroic-unofficial/core": "1.
|
|
25
|
-
"@trainheroic-unofficial/js": "1.
|
|
24
|
+
"@trainheroic-unofficial/core": "1.4.0",
|
|
25
|
+
"@trainheroic-unofficial/js": "1.4.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^26.0.0",
|