intervals-icu-mcp-server 0.1.9 → 0.2.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/client.d.ts +187 -0
- package/dist/client.js +46 -0
- package/dist/format.d.ts +2 -1
- package/dist/format.js +28 -10
- package/dist/index.js +45 -2
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -32,6 +32,59 @@ export declare const ActivitySchema: z.ZodObject<{
|
|
|
32
32
|
feel: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
33
33
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
34
|
trainer: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
35
|
+
decoupling: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
36
|
+
icu_efficiency_factor: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
37
|
+
icu_power_hr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
38
|
+
icu_variability_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
39
|
+
icu_pm_cp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
40
|
+
icu_rolling_ftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
41
|
+
polarization_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
42
|
+
icu_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
secs: z.ZodNumber;
|
|
45
|
+
}, "strip", z.ZodTypeAny, {
|
|
46
|
+
id: string;
|
|
47
|
+
secs: number;
|
|
48
|
+
}, {
|
|
49
|
+
id: string;
|
|
50
|
+
secs: number;
|
|
51
|
+
}>, "many">>>;
|
|
52
|
+
icu_hr_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
|
|
53
|
+
total_elevation_loss: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
54
|
+
max_speed: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
55
|
+
min_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
56
|
+
max_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
57
|
+
icu_joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
58
|
+
icu_hrr: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
59
|
+
hrr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
60
|
+
start_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
61
|
+
end_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
62
|
+
}, "strip", z.ZodTypeAny, {
|
|
63
|
+
hrr?: number | null | undefined;
|
|
64
|
+
start_bpm?: number | null | undefined;
|
|
65
|
+
end_bpm?: number | null | undefined;
|
|
66
|
+
}, {
|
|
67
|
+
hrr?: number | null | undefined;
|
|
68
|
+
start_bpm?: number | null | undefined;
|
|
69
|
+
end_bpm?: number | null | undefined;
|
|
70
|
+
}>>>;
|
|
71
|
+
strain_score: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
72
|
+
hr_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
73
|
+
power_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
74
|
+
gear: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
75
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
76
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
id?: string | null | undefined;
|
|
79
|
+
name?: string | null | undefined;
|
|
80
|
+
}, {
|
|
81
|
+
id?: string | null | undefined;
|
|
82
|
+
name?: string | null | undefined;
|
|
83
|
+
}>>>;
|
|
84
|
+
device_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
85
|
+
power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
86
|
+
source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
87
|
+
compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
35
88
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
36
89
|
id: z.ZodString;
|
|
37
90
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -61,6 +114,59 @@ export declare const ActivitySchema: z.ZodObject<{
|
|
|
61
114
|
feel: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
62
115
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
63
116
|
trainer: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
117
|
+
decoupling: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
118
|
+
icu_efficiency_factor: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
119
|
+
icu_power_hr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
120
|
+
icu_variability_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
121
|
+
icu_pm_cp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
122
|
+
icu_rolling_ftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
123
|
+
polarization_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
124
|
+
icu_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
125
|
+
id: z.ZodString;
|
|
126
|
+
secs: z.ZodNumber;
|
|
127
|
+
}, "strip", z.ZodTypeAny, {
|
|
128
|
+
id: string;
|
|
129
|
+
secs: number;
|
|
130
|
+
}, {
|
|
131
|
+
id: string;
|
|
132
|
+
secs: number;
|
|
133
|
+
}>, "many">>>;
|
|
134
|
+
icu_hr_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
|
|
135
|
+
total_elevation_loss: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
136
|
+
max_speed: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
137
|
+
min_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
138
|
+
max_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
139
|
+
icu_joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
140
|
+
icu_hrr: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
141
|
+
hrr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
142
|
+
start_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
143
|
+
end_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
144
|
+
}, "strip", z.ZodTypeAny, {
|
|
145
|
+
hrr?: number | null | undefined;
|
|
146
|
+
start_bpm?: number | null | undefined;
|
|
147
|
+
end_bpm?: number | null | undefined;
|
|
148
|
+
}, {
|
|
149
|
+
hrr?: number | null | undefined;
|
|
150
|
+
start_bpm?: number | null | undefined;
|
|
151
|
+
end_bpm?: number | null | undefined;
|
|
152
|
+
}>>>;
|
|
153
|
+
strain_score: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
154
|
+
hr_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
155
|
+
power_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
156
|
+
gear: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
157
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
158
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
159
|
+
}, "strip", z.ZodTypeAny, {
|
|
160
|
+
id?: string | null | undefined;
|
|
161
|
+
name?: string | null | undefined;
|
|
162
|
+
}, {
|
|
163
|
+
id?: string | null | undefined;
|
|
164
|
+
name?: string | null | undefined;
|
|
165
|
+
}>>>;
|
|
166
|
+
device_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
167
|
+
power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
168
|
+
source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
169
|
+
compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
64
170
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
65
171
|
id: z.ZodString;
|
|
66
172
|
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -90,6 +196,59 @@ export declare const ActivitySchema: z.ZodObject<{
|
|
|
90
196
|
feel: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
91
197
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
92
198
|
trainer: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
199
|
+
decoupling: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
200
|
+
icu_efficiency_factor: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
201
|
+
icu_power_hr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
202
|
+
icu_variability_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
203
|
+
icu_pm_cp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
204
|
+
icu_rolling_ftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
205
|
+
polarization_index: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
206
|
+
icu_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
207
|
+
id: z.ZodString;
|
|
208
|
+
secs: z.ZodNumber;
|
|
209
|
+
}, "strip", z.ZodTypeAny, {
|
|
210
|
+
id: string;
|
|
211
|
+
secs: number;
|
|
212
|
+
}, {
|
|
213
|
+
id: string;
|
|
214
|
+
secs: number;
|
|
215
|
+
}>, "many">>>;
|
|
216
|
+
icu_hr_zone_times: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
|
|
217
|
+
total_elevation_loss: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
218
|
+
max_speed: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
219
|
+
min_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
220
|
+
max_altitude: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
221
|
+
icu_joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
222
|
+
icu_hrr: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
223
|
+
hrr: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
224
|
+
start_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
225
|
+
end_bpm: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
226
|
+
}, "strip", z.ZodTypeAny, {
|
|
227
|
+
hrr?: number | null | undefined;
|
|
228
|
+
start_bpm?: number | null | undefined;
|
|
229
|
+
end_bpm?: number | null | undefined;
|
|
230
|
+
}, {
|
|
231
|
+
hrr?: number | null | undefined;
|
|
232
|
+
start_bpm?: number | null | undefined;
|
|
233
|
+
end_bpm?: number | null | undefined;
|
|
234
|
+
}>>>;
|
|
235
|
+
strain_score: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
236
|
+
hr_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
237
|
+
power_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
238
|
+
gear: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
239
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
240
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
241
|
+
}, "strip", z.ZodTypeAny, {
|
|
242
|
+
id?: string | null | undefined;
|
|
243
|
+
name?: string | null | undefined;
|
|
244
|
+
}, {
|
|
245
|
+
id?: string | null | undefined;
|
|
246
|
+
name?: string | null | undefined;
|
|
247
|
+
}>>>;
|
|
248
|
+
device_name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
249
|
+
power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
250
|
+
source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
251
|
+
compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
93
252
|
}, z.ZodTypeAny, "passthrough">>;
|
|
94
253
|
export type Activity = z.infer<typeof ActivitySchema>;
|
|
95
254
|
export declare const WellnessSchema: z.ZodObject<{
|
|
@@ -343,6 +502,34 @@ export declare function getActivities(config: ClientConfig, params: {
|
|
|
343
502
|
}): Promise<Activity[]>;
|
|
344
503
|
export declare function getActivityDetails(config: ClientConfig, activityId: string): Promise<Activity>;
|
|
345
504
|
export declare function getActivityIntervals(config: ClientConfig, activityId: string): Promise<z.infer<typeof IntervalSchema>>;
|
|
505
|
+
export declare const MessageSchema: z.ZodObject<{
|
|
506
|
+
id: z.ZodOptional<z.ZodNumber>;
|
|
507
|
+
athlete_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
508
|
+
name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
509
|
+
created: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
510
|
+
type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
511
|
+
content: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
512
|
+
attachment_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
513
|
+
}, "strip", z.ZodTypeAny, {
|
|
514
|
+
id?: number | undefined;
|
|
515
|
+
name?: string | null | undefined;
|
|
516
|
+
type?: string | null | undefined;
|
|
517
|
+
athlete_id?: string | null | undefined;
|
|
518
|
+
created?: string | null | undefined;
|
|
519
|
+
content?: string | null | undefined;
|
|
520
|
+
attachment_url?: string | null | undefined;
|
|
521
|
+
}, {
|
|
522
|
+
id?: number | undefined;
|
|
523
|
+
name?: string | null | undefined;
|
|
524
|
+
type?: string | null | undefined;
|
|
525
|
+
athlete_id?: string | null | undefined;
|
|
526
|
+
created?: string | null | undefined;
|
|
527
|
+
content?: string | null | undefined;
|
|
528
|
+
attachment_url?: string | null | undefined;
|
|
529
|
+
}>;
|
|
530
|
+
export type Message = z.infer<typeof MessageSchema>;
|
|
531
|
+
export declare function getActivityMessages(config: ClientConfig, activityId: string): Promise<Message[]>;
|
|
532
|
+
export declare function addActivityMessage(config: ClientConfig, activityId: string, content: string): Promise<void>;
|
|
346
533
|
export declare function getWellness(config: ClientConfig, params: {
|
|
347
534
|
startDate: string;
|
|
348
535
|
endDate: string;
|
package/dist/client.js
CHANGED
|
@@ -53,6 +53,36 @@ export const ActivitySchema = z.object({
|
|
|
53
53
|
feel: z.number().nullable().optional(),
|
|
54
54
|
description: z.string().nullable().optional(),
|
|
55
55
|
trainer: z.boolean().nullable().optional(),
|
|
56
|
+
decoupling: z.number().nullable().optional(),
|
|
57
|
+
icu_efficiency_factor: z.number().nullable().optional(),
|
|
58
|
+
icu_power_hr: z.number().nullable().optional(),
|
|
59
|
+
icu_variability_index: z.number().nullable().optional(),
|
|
60
|
+
icu_pm_cp: z.number().nullable().optional(),
|
|
61
|
+
icu_rolling_ftp: z.number().nullable().optional(),
|
|
62
|
+
polarization_index: z.number().nullable().optional(),
|
|
63
|
+
icu_zone_times: z.array(z.object({ id: z.string(), secs: z.number() })).nullable().optional(),
|
|
64
|
+
icu_hr_zone_times: z.array(z.number()).nullable().optional(),
|
|
65
|
+
total_elevation_loss: z.number().nullable().optional(),
|
|
66
|
+
max_speed: z.number().nullable().optional(),
|
|
67
|
+
min_altitude: z.number().nullable().optional(),
|
|
68
|
+
max_altitude: z.number().nullable().optional(),
|
|
69
|
+
icu_joules: z.number().nullable().optional(),
|
|
70
|
+
icu_hrr: z.object({
|
|
71
|
+
hrr: z.number().nullable().optional(),
|
|
72
|
+
start_bpm: z.number().nullable().optional(),
|
|
73
|
+
end_bpm: z.number().nullable().optional(),
|
|
74
|
+
}).nullable().optional(),
|
|
75
|
+
strain_score: z.number().nullable().optional(),
|
|
76
|
+
hr_load: z.number().nullable().optional(),
|
|
77
|
+
power_load: z.number().nullable().optional(),
|
|
78
|
+
gear: z.object({
|
|
79
|
+
id: z.string().nullable().optional(),
|
|
80
|
+
name: z.string().nullable().optional(),
|
|
81
|
+
}).nullable().optional(),
|
|
82
|
+
device_name: z.string().nullable().optional(),
|
|
83
|
+
power_meter: z.string().nullable().optional(),
|
|
84
|
+
source: z.string().nullable().optional(),
|
|
85
|
+
compliance: z.number().nullable().optional(),
|
|
56
86
|
}).passthrough();
|
|
57
87
|
export const WellnessSchema = z.object({
|
|
58
88
|
id: z.string().optional(),
|
|
@@ -138,6 +168,22 @@ export async function getActivityIntervals(config, activityId) {
|
|
|
138
168
|
const data = await request(`/activity/${activityId}/intervals`, config);
|
|
139
169
|
return IntervalSchema.parse(data);
|
|
140
170
|
}
|
|
171
|
+
export const MessageSchema = z.object({
|
|
172
|
+
id: z.number().optional(),
|
|
173
|
+
athlete_id: z.string().nullable().optional(),
|
|
174
|
+
name: z.string().nullable().optional(),
|
|
175
|
+
created: z.string().nullable().optional(),
|
|
176
|
+
type: z.string().nullable().optional(),
|
|
177
|
+
content: z.string().nullable().optional(),
|
|
178
|
+
attachment_url: z.string().nullable().optional(),
|
|
179
|
+
});
|
|
180
|
+
export async function getActivityMessages(config, activityId) {
|
|
181
|
+
const data = await request(`/activity/${activityId}/messages`, config);
|
|
182
|
+
return z.array(MessageSchema).parse(data);
|
|
183
|
+
}
|
|
184
|
+
export async function addActivityMessage(config, activityId, content) {
|
|
185
|
+
await mutate("POST", `/activity/${activityId}/messages`, config, { content });
|
|
186
|
+
}
|
|
141
187
|
export async function getWellness(config, params) {
|
|
142
188
|
const data = await request(`/athlete/${config.athleteId}/wellness`, config, { oldest: params.startDate, newest: params.endDate });
|
|
143
189
|
if (Array.isArray(data)) {
|
package/dist/format.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { Activity, Wellness, Event, SportSettings } from "./client.js";
|
|
1
|
+
import type { Activity, Wellness, Event, SportSettings, Message } from "./client.js";
|
|
2
2
|
export declare function formatActivity(a: Activity): string;
|
|
3
3
|
export declare function formatWellness(date: string, w: Wellness): string;
|
|
4
4
|
export declare function formatEvent(e: Event): string;
|
|
5
|
+
export declare function formatMessage(m: Message): string;
|
|
5
6
|
export declare function formatSportZones(s: SportSettings): string;
|
|
6
7
|
export declare function formatIntervalRow(interval: Record<string, unknown>, idx: number): string;
|
package/dist/format.js
CHANGED
|
@@ -3,6 +3,13 @@ function n(v, unit = "") {
|
|
|
3
3
|
return "N/A";
|
|
4
4
|
return `${v}${unit}`;
|
|
5
5
|
}
|
|
6
|
+
function fmtDuration(secs) {
|
|
7
|
+
if (secs == null)
|
|
8
|
+
return "N/A";
|
|
9
|
+
const h = Math.floor(secs / 3600);
|
|
10
|
+
const m = Math.round((secs % 3600) / 60);
|
|
11
|
+
return h > 0 ? `${h}h${m}m` : `${m}m`;
|
|
12
|
+
}
|
|
6
13
|
export function formatActivity(a) {
|
|
7
14
|
const startTime = a.start_date_local
|
|
8
15
|
? new Date(a.start_date_local).toLocaleString()
|
|
@@ -11,23 +18,30 @@ export function formatActivity(a) {
|
|
|
11
18
|
`**${a.name ?? "Unnamed"}** (${a.id})`,
|
|
12
19
|
`Type: ${n(a.type)} | Date: ${startTime}`,
|
|
13
20
|
`Distance: ${n(a.distance, " m")} | Duration: ${n(a.elapsed_time, " s")} | Moving: ${n(a.moving_time, " s")}`,
|
|
14
|
-
`Elevation:
|
|
21
|
+
`Elevation: +${n(a.total_elevation_gain, " m")} / -${n(a.total_elevation_loss, " m")} | Altitude: ${n(a.min_altitude, " m")}-${n(a.max_altitude, " m")}`,
|
|
15
22
|
"",
|
|
16
23
|
"Power:",
|
|
17
24
|
` Avg: ${n(a.icu_average_watts, " W")} | Weighted Avg: ${n(a.icu_weighted_avg_watts, " W")} | FTP: ${n(a.icu_ftp, " W")}`,
|
|
18
|
-
` Training Load: ${n(a.icu_training_load)} | Intensity: ${n(a.icu_intensity)}`,
|
|
25
|
+
` Training Load: ${n(a.icu_training_load)} | Intensity: ${n(a.icu_intensity)} | VI: ${n(a.icu_variability_index)}`,
|
|
26
|
+
` Critical Power: ${n(a.icu_pm_cp, " W")} | Rolling FTP: ${n(a.icu_rolling_ftp, " W")} | Energy: ${a.icu_joules != null ? `${Math.round(a.icu_joules / 1000)} kJ` : "N/A"}`,
|
|
19
27
|
"",
|
|
20
28
|
"Heart Rate:",
|
|
21
29
|
` Avg: ${n(a.average_heartrate, " bpm")} | Max: ${n(a.max_heartrate, " bpm")}`,
|
|
22
|
-
""
|
|
23
|
-
"Other:",
|
|
24
|
-
` Cadence: ${n(a.average_cadence, " rpm")} | Calories: ${n(a.calories)} | Speed: ${n(a.average_speed, " m/s")}`,
|
|
25
|
-
` Temp: ${n(a.average_temp, "°C")} | Trainer: ${a.trainer ? "Yes" : "No"}`,
|
|
26
|
-
` RPE: ${n(a.icu_rpe ?? a.perceived_exertion)}/10 | Feel: ${n(a.feel)}/5`,
|
|
27
|
-
"",
|
|
28
|
-
"Fitness:",
|
|
29
|
-
` CTL: ${n(a.icu_ctl)} | ATL: ${n(a.icu_atl)} | TRIMP: ${n(a.trimp)}`,
|
|
30
|
+
` Decoupling: ${n(a.decoupling, "%")} | Efficiency Factor: ${n(a.icu_efficiency_factor)} | Pw:Hr: ${n(a.icu_power_hr)}`,
|
|
30
31
|
];
|
|
32
|
+
if (a.icu_hrr?.hrr != null) {
|
|
33
|
+
lines.push(` HR Recovery: -${n(a.icu_hrr.hrr, " bpm")} (${n(a.icu_hrr.start_bpm)} → ${n(a.icu_hrr.end_bpm)} bpm)`);
|
|
34
|
+
}
|
|
35
|
+
if (a.icu_zone_times?.length || a.icu_hr_zone_times?.length) {
|
|
36
|
+
lines.push("", "Zones:");
|
|
37
|
+
if (a.icu_zone_times?.length) {
|
|
38
|
+
lines.push(` Power: ${a.icu_zone_times.map((z) => `${z.id} ${fmtDuration(z.secs)}`).join(" | ")}`);
|
|
39
|
+
}
|
|
40
|
+
if (a.icu_hr_zone_times?.length) {
|
|
41
|
+
lines.push(` HR: ${a.icu_hr_zone_times.map((secs, i) => `Z${i + 1} ${fmtDuration(secs)}`).join(" | ")}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
lines.push("", "Other:", ` Cadence: ${n(a.average_cadence, " rpm")} | Calories: ${n(a.calories)} | Speed: ${n(a.average_speed, " m/s")} (max ${n(a.max_speed, " m/s")})`, ` Temp: ${n(a.average_temp, "°C")} | Trainer: ${a.trainer ? "Yes" : "No"}`, ` RPE: ${n(a.icu_rpe ?? a.perceived_exertion)}/10 | Feel: ${n(a.feel)}/5`, "", "Fitness:", ` CTL: ${n(a.icu_ctl)} | ATL: ${n(a.icu_atl)} | TRIMP: ${n(a.trimp)} | Polarization Index: ${n(a.polarization_index)}`, ` Power Load: ${n(a.power_load)} | HR Load: ${n(a.hr_load)} | Strain: ${n(a.strain_score)}`, "", "Equipment:", ` Gear: ${n(a.gear?.name)} | Device: ${n(a.device_name)} | Power Meter: ${n(a.power_meter)}`, ` Source: ${n(a.source)} | Compliance: ${a.compliance != null ? `${a.compliance}%` : "N/A"}`);
|
|
31
45
|
if (a.description)
|
|
32
46
|
lines.push("", `Description: ${a.description}`);
|
|
33
47
|
return lines.join("\n");
|
|
@@ -83,6 +97,10 @@ export function formatEvent(e) {
|
|
|
83
97
|
.filter(Boolean)
|
|
84
98
|
.join("\n");
|
|
85
99
|
}
|
|
100
|
+
export function formatMessage(m) {
|
|
101
|
+
const when = m.created ? new Date(m.created).toLocaleString() : "Unknown time";
|
|
102
|
+
return `**${m.name ?? "Unknown"}** (${when}): ${m.content ?? ""}`;
|
|
103
|
+
}
|
|
86
104
|
function zoneRanges(breakpoints, names) {
|
|
87
105
|
const count = names?.length ?? (breakpoints?.length ?? 0) + 1;
|
|
88
106
|
const ranges = [];
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { getActivities, getActivityDetails, getActivityIntervals, getWellness, getEvents, getEventById, getNotesByDate, createEvent, updateEvent, deleteEvent, getAthleteProfile, getAthleteZones, getAthleteSummary, getWellnessDay, updateWellness, getActivityStreams, getActivityPowerCurves, listWorkouts, createWorkout, bulkCreateEvents, bulkDeleteEvents, getAthleteFitness, } from "./client.js";
|
|
8
|
-
import { formatActivity, formatWellness, formatEvent, formatIntervalRow, formatSportZones, } from "./format.js";
|
|
7
|
+
import { getActivities, getActivityDetails, getActivityIntervals, getWellness, getEvents, getEventById, getNotesByDate, createEvent, updateEvent, deleteEvent, getAthleteProfile, getAthleteZones, getAthleteSummary, getWellnessDay, updateWellness, getActivityStreams, getActivityPowerCurves, listWorkouts, createWorkout, bulkCreateEvents, bulkDeleteEvents, getAthleteFitness, getActivityMessages, addActivityMessage, } from "./client.js";
|
|
8
|
+
import { formatActivity, formatWellness, formatEvent, formatIntervalRow, formatSportZones, formatMessage, } from "./format.js";
|
|
9
9
|
function getConfig(args) {
|
|
10
10
|
const apiKey = args["api_key"] ?? process.env.API_KEY ?? "";
|
|
11
11
|
const athleteId = args["athlete_id"] ??
|
|
@@ -282,6 +282,33 @@ const TOOLS = [
|
|
|
282
282
|
},
|
|
283
283
|
},
|
|
284
284
|
},
|
|
285
|
+
{
|
|
286
|
+
name: "get_activity_messages",
|
|
287
|
+
description: "Get the comments/notes left on a specific activity (the chat thread shown under an activity in Intervals.icu, e.g. coach feedback).",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
required: ["activity_id"],
|
|
291
|
+
properties: {
|
|
292
|
+
activity_id: { type: "string", description: "The activity ID" },
|
|
293
|
+
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
294
|
+
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: "add_activity_message",
|
|
300
|
+
description: "Post a comment/note on a specific activity (adds to the activity's chat thread in Intervals.icu).",
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: "object",
|
|
303
|
+
required: ["activity_id", "content"],
|
|
304
|
+
properties: {
|
|
305
|
+
activity_id: { type: "string", description: "The activity ID" },
|
|
306
|
+
content: { type: "string", description: "Comment text to post" },
|
|
307
|
+
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
308
|
+
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
285
312
|
{
|
|
286
313
|
name: "list_workouts",
|
|
287
314
|
description: "List all workouts in the athlete's workout library.",
|
|
@@ -624,6 +651,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
624
651
|
const curves = await getActivityPowerCurves(config, activityId);
|
|
625
652
|
return { content: [{ type: "text", text: `Power curves for activity ${activityId}:\n\n${JSON.stringify(curves, null, 2)}` }] };
|
|
626
653
|
}
|
|
654
|
+
case "get_activity_messages": {
|
|
655
|
+
const activityId = z.string().parse(args["activity_id"]);
|
|
656
|
+
const config = getConfig(args);
|
|
657
|
+
const messages = await getActivityMessages(config, activityId);
|
|
658
|
+
if (!messages.length)
|
|
659
|
+
return { content: [{ type: "text", text: "No comments on this activity." }] };
|
|
660
|
+
const text = messages.map(formatMessage).join("\n");
|
|
661
|
+
return { content: [{ type: "text", text }] };
|
|
662
|
+
}
|
|
663
|
+
case "add_activity_message": {
|
|
664
|
+
const activityId = z.string().parse(args["activity_id"]);
|
|
665
|
+
const content = z.string().parse(args["content"]);
|
|
666
|
+
const config = getConfig(args);
|
|
667
|
+
await addActivityMessage(config, activityId, content);
|
|
668
|
+
return { content: [{ type: "text", text: `Comment added to activity ${activityId}.` }] };
|
|
669
|
+
}
|
|
627
670
|
case "list_workouts": {
|
|
628
671
|
const config = getConfig(args);
|
|
629
672
|
const workouts = await listWorkouts(config);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intervals-icu-mcp-server",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server for Intervals.icu —
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for Intervals.icu — 24 tools for activities, wellness, calendar events, workouts and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"intervals-icu-mcp": "dist/index.js"
|