intervals-icu-mcp-server 0.1.8 → 0.1.9
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/client.js +1 -1
- package/dist/format.js +10 -6
- package/dist/index.js +11 -5
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -144,7 +144,7 @@ export async function getWellness(config, params) {
|
|
|
144
144
|
const result = {};
|
|
145
145
|
for (const entry of data) {
|
|
146
146
|
const parsed = WellnessSchema.parse(entry);
|
|
147
|
-
const key =
|
|
147
|
+
const key = parsed.id ?? String(Object.keys(result).length);
|
|
148
148
|
result[key] = parsed;
|
|
149
149
|
}
|
|
150
150
|
return result;
|
package/dist/format.js
CHANGED
|
@@ -110,19 +110,23 @@ export function formatSportZones(s) {
|
|
|
110
110
|
lines.push("");
|
|
111
111
|
}
|
|
112
112
|
if (s.hr_zones?.length) {
|
|
113
|
+
// Unlike power_zones (%FTP), hr_zones stores absolute bpm thresholds directly.
|
|
113
114
|
lines.push(`HR Zones${s.lthr != null ? ` (LTHR: ${s.lthr}bpm)` : ""}:`);
|
|
114
115
|
for (const z of zoneRanges(s.hr_zones, s.hr_zone_names)) {
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
116
|
+
const pctFrom = s.lthr != null ? Math.round((z.from / s.lthr) * 100) : null;
|
|
117
|
+
const pctTo = s.lthr != null && z.to != null ? Math.round((z.to / s.lthr) * 100) : null;
|
|
118
|
+
const pct = pctFrom != null ? ` (${pctFrom}${pctTo != null ? "-" + pctTo : ""}% LTHR)` : "";
|
|
119
|
+
const bpm = z.to != null ? `${z.from}-${z.to} bpm` : `${z.from}+ bpm`;
|
|
120
|
+
lines.push(` ${z.name}: ${bpm}${pct}`);
|
|
118
121
|
}
|
|
119
122
|
lines.push("");
|
|
120
123
|
}
|
|
121
124
|
if (s.pace_zones?.length) {
|
|
122
|
-
|
|
125
|
+
// Same as hr_zones — absolute thresholds, not percentages of threshold_pace.
|
|
126
|
+
lines.push(`Pace Zones${s.threshold_pace != null ? ` (Threshold: ${s.threshold_pace} m/s)` : ""}:`);
|
|
123
127
|
for (const z of zoneRanges(s.pace_zones, s.pace_zone_names)) {
|
|
124
|
-
const
|
|
125
|
-
lines.push(` ${z.name}: ${
|
|
128
|
+
const range = z.to != null ? `${z.from}-${z.to} m/s` : `${z.from}+ m/s`;
|
|
129
|
+
lines.push(` ${z.name}: ${range}`);
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
return lines.join("\n").trimEnd();
|
package/dist/index.js
CHANGED
|
@@ -17,18 +17,24 @@ function getConfig(args) {
|
|
|
17
17
|
throw new Error("No athlete ID provided. Set ATHLETE_ID in .env or pass athlete_id.");
|
|
18
18
|
return { apiKey, athleteId };
|
|
19
19
|
}
|
|
20
|
+
function toDateStr(d) {
|
|
21
|
+
const y = d.getFullYear();
|
|
22
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
23
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
24
|
+
return `${y}-${m}-${day}`;
|
|
25
|
+
}
|
|
20
26
|
function today() {
|
|
21
|
-
return new Date()
|
|
27
|
+
return toDateStr(new Date());
|
|
22
28
|
}
|
|
23
29
|
function daysAgo(n) {
|
|
24
30
|
const d = new Date();
|
|
25
31
|
d.setDate(d.getDate() - n);
|
|
26
|
-
return d
|
|
32
|
+
return toDateStr(d);
|
|
27
33
|
}
|
|
28
34
|
function daysFromNow(n) {
|
|
29
35
|
const d = new Date();
|
|
30
36
|
d.setDate(d.getDate() + n);
|
|
31
|
-
return d
|
|
37
|
+
return toDateStr(d);
|
|
32
38
|
}
|
|
33
39
|
const TOOLS = [
|
|
34
40
|
{
|
|
@@ -125,7 +131,7 @@ const TOOLS = [
|
|
|
125
131
|
category: { type: "string", description: "Event category: WORKOUT, NOTE, RACE_A, RACE_B, RACE_C (race priority A/B/C — this is what makes it a race, there is no separate race flag), PLAN, HOLIDAY, SICK, INJURED, TARGET, and others" },
|
|
126
132
|
type: { type: "string", description: "Activity type: Ride, Run, Swim, WeightTraining, etc." },
|
|
127
133
|
distance: { type: "number", description: "Distance in meters (e.g. 100000 for 100km)" },
|
|
128
|
-
sub_type: { type: "string", description: "
|
|
134
|
+
sub_type: { type: "string", description: "One of NONE, COMMUTE, WARMUP, COOLDOWN, RACE. Not race priority — that's set via category (RACE_A/RACE_B/RACE_C)." },
|
|
129
135
|
color: { type: "string", description: "Event color, e.g. '#1f77b4' (hex) or a named color like 'blue', 'green', 'sky'" },
|
|
130
136
|
workout_doc: { type: "object", description: "Pre-computed Intervals.icu workout_doc (as returned by get_event_by_id/list_workouts on an existing structured workout) to copy verbatim onto this event. Hand-written partial docs won't render correctly — prefer setting `description` with the Workout Builder syntax and omitting this field so Intervals.icu computes it." },
|
|
131
137
|
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
@@ -148,7 +154,7 @@ const TOOLS = [
|
|
|
148
154
|
category: { type: "string", description: "New category" },
|
|
149
155
|
type: { type: "string", description: "New activity type" },
|
|
150
156
|
distance: { type: "number", description: "Distance in meters (e.g. 100000 for 100km)" },
|
|
151
|
-
sub_type: { type: "string", description: "
|
|
157
|
+
sub_type: { type: "string", description: "One of NONE, COMMUTE, WARMUP, COOLDOWN, RACE. Not race priority — that's set via category (RACE_A/RACE_B/RACE_C)." },
|
|
152
158
|
color: { type: "string", description: "Event color, e.g. '#1f77b4' (hex) or a named color like 'blue', 'green', 'sky'" },
|
|
153
159
|
workout_doc: { type: "object", description: "Pre-computed Intervals.icu workout_doc to copy verbatim. Hand-written partial docs won't render correctly — prefer updating `description` with Workout Builder syntax instead." },
|
|
154
160
|
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|