intervals-icu-mcp-server 0.2.1 → 0.3.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/client.d.ts CHANGED
@@ -85,6 +85,7 @@ export declare const ActivitySchema: z.ZodObject<{
85
85
  power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
86
86
  source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
87
87
  compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
88
+ icu_chat_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
88
89
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
89
90
  id: z.ZodString;
90
91
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -167,6 +168,7 @@ export declare const ActivitySchema: z.ZodObject<{
167
168
  power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
168
169
  source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
169
170
  compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
171
+ icu_chat_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
170
172
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
171
173
  id: z.ZodString;
172
174
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -249,6 +251,7 @@ export declare const ActivitySchema: z.ZodObject<{
249
251
  power_meter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
250
252
  source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
251
253
  compliance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
254
+ icu_chat_id: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
252
255
  }, z.ZodTypeAny, "passthrough">>;
253
256
  export type Activity = z.infer<typeof ActivitySchema>;
254
257
  export declare const WellnessSchema: z.ZodObject<{
@@ -274,6 +277,24 @@ export declare const WellnessSchema: z.ZodObject<{
274
277
  bodyFat: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
275
278
  spO2: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
276
279
  comments: z.ZodOptional<z.ZodNullable<z.ZodString>>;
280
+ rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
281
+ avgSleepingHR: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
282
+ sportInfo: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
283
+ type: z.ZodOptional<z.ZodString>;
284
+ eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
285
+ wPrime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
286
+ pMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
287
+ }, "strip", z.ZodTypeAny, {
288
+ type?: string | undefined;
289
+ eftp?: number | null | undefined;
290
+ wPrime?: number | null | undefined;
291
+ pMax?: number | null | undefined;
292
+ }, {
293
+ type?: string | undefined;
294
+ eftp?: number | null | undefined;
295
+ wPrime?: number | null | undefined;
296
+ pMax?: number | null | undefined;
297
+ }>, "many">>>;
277
298
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
278
299
  id: z.ZodOptional<z.ZodString>;
279
300
  ctl: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -297,6 +318,24 @@ export declare const WellnessSchema: z.ZodObject<{
297
318
  bodyFat: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
298
319
  spO2: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
299
320
  comments: z.ZodOptional<z.ZodNullable<z.ZodString>>;
321
+ rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
322
+ avgSleepingHR: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
323
+ sportInfo: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
324
+ type: z.ZodOptional<z.ZodString>;
325
+ eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
326
+ wPrime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
327
+ pMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
328
+ }, "strip", z.ZodTypeAny, {
329
+ type?: string | undefined;
330
+ eftp?: number | null | undefined;
331
+ wPrime?: number | null | undefined;
332
+ pMax?: number | null | undefined;
333
+ }, {
334
+ type?: string | undefined;
335
+ eftp?: number | null | undefined;
336
+ wPrime?: number | null | undefined;
337
+ pMax?: number | null | undefined;
338
+ }>, "many">>>;
300
339
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
301
340
  id: z.ZodOptional<z.ZodString>;
302
341
  ctl: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -320,6 +359,24 @@ export declare const WellnessSchema: z.ZodObject<{
320
359
  bodyFat: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
321
360
  spO2: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
322
361
  comments: z.ZodOptional<z.ZodNullable<z.ZodString>>;
362
+ rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
363
+ avgSleepingHR: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
364
+ sportInfo: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
365
+ type: z.ZodOptional<z.ZodString>;
366
+ eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
367
+ wPrime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
368
+ pMax: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
369
+ }, "strip", z.ZodTypeAny, {
370
+ type?: string | undefined;
371
+ eftp?: number | null | undefined;
372
+ wPrime?: number | null | undefined;
373
+ pMax?: number | null | undefined;
374
+ }, {
375
+ type?: string | undefined;
376
+ eftp?: number | null | undefined;
377
+ wPrime?: number | null | undefined;
378
+ pMax?: number | null | undefined;
379
+ }>, "many">>>;
323
380
  }, z.ZodTypeAny, "passthrough">>;
324
381
  export type Wellness = z.infer<typeof WellnessSchema>;
325
382
  export declare const EventSchema: z.ZodObject<{
@@ -332,6 +389,11 @@ export declare const EventSchema: z.ZodObject<{
332
389
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
333
390
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
334
391
  color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
392
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
393
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
394
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
395
+ icu_intensity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
396
+ paired_activity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
335
397
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
336
398
  id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
337
399
  date: z.ZodOptional<z.ZodString>;
@@ -342,6 +404,11 @@ export declare const EventSchema: z.ZodObject<{
342
404
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
343
405
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
344
406
  color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
407
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
408
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
409
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
410
+ icu_intensity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
411
+ paired_activity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
345
412
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
346
413
  id: z.ZodUnion<[z.ZodString, z.ZodNumber]>;
347
414
  date: z.ZodOptional<z.ZodString>;
@@ -352,6 +419,11 @@ export declare const EventSchema: z.ZodObject<{
352
419
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
353
420
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
354
421
  color: z.ZodOptional<z.ZodNullable<z.ZodString>>;
422
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
423
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
424
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
425
+ icu_intensity: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
426
+ paired_activity_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
355
427
  }, z.ZodTypeAny, "passthrough">>;
356
428
  export type Event = z.infer<typeof EventSchema>;
357
429
  export declare const IntervalSchema: z.ZodObject<{
@@ -423,6 +495,10 @@ export declare const SportSettingsSchema: z.ZodObject<{
423
495
  hr_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
424
496
  pace_zones: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
425
497
  pace_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
498
+ w_prime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
499
+ sweet_spot_min: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
500
+ sweet_spot_max: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
501
+ best_effort_distances: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
426
502
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
427
503
  types: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
428
504
  ftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -435,6 +511,10 @@ export declare const SportSettingsSchema: z.ZodObject<{
435
511
  hr_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
436
512
  pace_zones: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
437
513
  pace_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
514
+ w_prime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
515
+ sweet_spot_min: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
516
+ sweet_spot_max: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
517
+ best_effort_distances: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
438
518
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
439
519
  types: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
440
520
  ftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -447,6 +527,10 @@ export declare const SportSettingsSchema: z.ZodObject<{
447
527
  hr_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
448
528
  pace_zones: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
449
529
  pace_zone_names: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
530
+ w_prime: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
531
+ sweet_spot_min: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
532
+ sweet_spot_max: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
533
+ best_effort_distances: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodNumber, "many">>>;
450
534
  }, z.ZodTypeAny, "passthrough">>;
451
535
  export type SportSettings = z.infer<typeof SportSettingsSchema>;
452
536
  export declare const WorkoutSchema: z.ZodObject<{
@@ -455,18 +539,27 @@ export declare const WorkoutSchema: z.ZodObject<{
455
539
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
456
540
  type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
457
541
  updated: z.ZodOptional<z.ZodNullable<z.ZodString>>;
542
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
543
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
544
+ joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
458
545
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
459
546
  id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
460
547
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
461
548
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
462
549
  type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
463
550
  updated: z.ZodOptional<z.ZodNullable<z.ZodString>>;
551
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
552
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
553
+ joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
464
554
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
465
555
  id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
466
556
  name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
467
557
  description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
468
558
  type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
469
559
  updated: z.ZodOptional<z.ZodNullable<z.ZodString>>;
560
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
561
+ icu_training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
562
+ joules: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
470
563
  }, z.ZodTypeAny, "passthrough">>;
471
564
  export type Workout = z.infer<typeof WorkoutSchema>;
472
565
  export interface WorkoutInput {
@@ -530,6 +623,8 @@ export declare const MessageSchema: z.ZodObject<{
530
623
  export type Message = z.infer<typeof MessageSchema>;
531
624
  export declare function getActivityMessages(config: ClientConfig, activityId: string): Promise<Message[]>;
532
625
  export declare function addActivityMessage(config: ClientConfig, activityId: string, content: string): Promise<void>;
626
+ export declare function deleteActivityMessage(config: ClientConfig, activityId: string, messageId: number): Promise<void>;
627
+ export declare function updateActivityMessage(config: ClientConfig, activityId: string, messageId: number, content: string): Promise<void>;
533
628
  export declare function getWellness(config: ClientConfig, params: {
534
629
  startDate: string;
535
630
  endDate: string;
@@ -564,6 +659,10 @@ export declare const AthleteFitnessSchema: z.ZodObject<{
564
659
  eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
565
660
  eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
566
661
  weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
662
+ count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
663
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
664
+ calories: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
665
+ distance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
567
666
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
568
667
  date: z.ZodOptional<z.ZodString>;
569
668
  fitness: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -574,6 +673,10 @@ export declare const AthleteFitnessSchema: z.ZodObject<{
574
673
  eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
575
674
  eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
576
675
  weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
676
+ count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
677
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
678
+ calories: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
679
+ distance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
577
680
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
578
681
  date: z.ZodOptional<z.ZodString>;
579
682
  fitness: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
@@ -584,6 +687,10 @@ export declare const AthleteFitnessSchema: z.ZodObject<{
584
687
  eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
585
688
  eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
586
689
  weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
690
+ count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
691
+ moving_time: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
692
+ calories: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
693
+ distance: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
587
694
  }, z.ZodTypeAny, "passthrough">>;
588
695
  export type AthleteFitness = z.infer<typeof AthleteFitnessSchema>;
589
696
  export declare function getAthleteFitness(config: ClientConfig, params: {
package/dist/client.js CHANGED
@@ -83,6 +83,7 @@ export const ActivitySchema = z.object({
83
83
  power_meter: z.string().nullable().optional(),
84
84
  source: z.string().nullable().optional(),
85
85
  compliance: z.number().nullable().optional(),
86
+ icu_chat_id: z.number().nullable().optional(),
86
87
  }).passthrough();
87
88
  export const WellnessSchema = z.object({
88
89
  id: z.string().optional(),
@@ -107,6 +108,14 @@ export const WellnessSchema = z.object({
107
108
  bodyFat: z.number().nullable().optional(),
108
109
  spO2: z.number().nullable().optional(),
109
110
  comments: z.string().nullable().optional(),
111
+ rampRate: z.number().nullable().optional(),
112
+ avgSleepingHR: z.number().nullable().optional(),
113
+ sportInfo: z.array(z.object({
114
+ type: z.string().optional(),
115
+ eftp: z.number().nullable().optional(),
116
+ wPrime: z.number().nullable().optional(),
117
+ pMax: z.number().nullable().optional(),
118
+ })).nullable().optional(),
110
119
  }).passthrough();
111
120
  export const EventSchema = z.object({
112
121
  id: z.union([z.string(), z.number()]),
@@ -118,6 +127,11 @@ export const EventSchema = z.object({
118
127
  name: z.string().nullable().optional(),
119
128
  description: z.string().nullable().optional(),
120
129
  color: z.string().nullable().optional(),
130
+ type: z.string().nullable().optional(),
131
+ moving_time: z.number().nullable().optional(),
132
+ icu_training_load: z.number().nullable().optional(),
133
+ icu_intensity: z.number().nullable().optional(),
134
+ paired_activity_id: z.string().nullable().optional(),
121
135
  }).passthrough();
122
136
  export const IntervalSchema = z.object({
123
137
  icu_intervals: z.array(z.record(z.unknown())).nullable().optional(),
@@ -147,6 +161,10 @@ export const SportSettingsSchema = z.object({
147
161
  hr_zone_names: z.array(z.string()).nullable().optional(),
148
162
  pace_zones: z.array(z.number()).nullable().optional(),
149
163
  pace_zone_names: z.array(z.string()).nullable().optional(),
164
+ w_prime: z.number().nullable().optional(),
165
+ sweet_spot_min: z.number().nullable().optional(),
166
+ sweet_spot_max: z.number().nullable().optional(),
167
+ best_effort_distances: z.array(z.number()).nullable().optional(),
150
168
  }).passthrough();
151
169
  export const WorkoutSchema = z.object({
152
170
  id: z.union([z.string(), z.number()]).optional(),
@@ -154,6 +172,9 @@ export const WorkoutSchema = z.object({
154
172
  description: z.string().nullable().optional(),
155
173
  type: z.string().nullable().optional(),
156
174
  updated: z.string().nullable().optional(),
175
+ moving_time: z.number().nullable().optional(),
176
+ icu_training_load: z.number().nullable().optional(),
177
+ joules: z.number().nullable().optional(),
157
178
  }).passthrough();
158
179
  // ---- API methods ---- //
159
180
  export async function getActivities(config, params) {
@@ -184,6 +205,20 @@ export async function getActivityMessages(config, activityId) {
184
205
  export async function addActivityMessage(config, activityId, content) {
185
206
  await mutate("POST", `/activity/${activityId}/messages`, config, { content });
186
207
  }
208
+ // Intervals.icu has no endpoint to edit a message's content in place — only
209
+ // delete and re-add. Callers wanting an "edit" should use updateActivityMessage,
210
+ // which does both and leaves the old message showing as deleted.
211
+ export async function deleteActivityMessage(config, activityId, messageId) {
212
+ const activity = await getActivityDetails(config, activityId);
213
+ const chatId = activity.icu_chat_id;
214
+ if (chatId == null)
215
+ throw new Error(`Activity ${activityId} has no chat/messages.`);
216
+ await mutate("DELETE", `/chats/${chatId}/messages/${messageId}`, config);
217
+ }
218
+ export async function updateActivityMessage(config, activityId, messageId, content) {
219
+ await deleteActivityMessage(config, activityId, messageId);
220
+ await addActivityMessage(config, activityId, content);
221
+ }
187
222
  export async function getWellness(config, params) {
188
223
  const data = await request(`/athlete/${config.athleteId}/wellness`, config, { oldest: params.startDate, newest: params.endDate });
189
224
  if (Array.isArray(data)) {
@@ -303,6 +338,10 @@ export const AthleteFitnessSchema = z.object({
303
338
  eftp: z.number().nullable().optional(),
304
339
  eftpPerKg: z.number().nullable().optional(),
305
340
  weight: z.number().nullable().optional(),
341
+ count: z.number().nullable().optional(),
342
+ moving_time: z.number().nullable().optional(),
343
+ calories: z.number().nullable().optional(),
344
+ distance: z.number().nullable().optional(),
306
345
  }).passthrough();
307
346
  export async function getAthleteFitness(config, params) {
308
347
  const data = await request(`/athlete/${config.athleteId}/athlete-summary.json`, config, { start: params.startDate, end: params.endDate });
package/dist/format.js CHANGED
@@ -48,14 +48,14 @@ export function formatActivity(a) {
48
48
  }
49
49
  export function formatWellness(date, w) {
50
50
  const sleepHours = w.sleepSecs != null ? (w.sleepSecs / 3600).toFixed(1) + " h" : "N/A";
51
- return [
51
+ const lines = [
52
52
  `**${date}**`,
53
53
  "",
54
54
  "Training:",
55
- ` CTL: ${n(w.ctl)} | ATL: ${n(w.atl)}`,
55
+ ` CTL: ${n(w.ctl)} | ATL: ${n(w.atl)} | Ramp Rate: ${n(w.rampRate)}`,
56
56
  "",
57
57
  "Vitals:",
58
- ` Weight: ${n(w.weight, " kg")} | Resting HR: ${n(w.restingHR, " bpm")}`,
58
+ ` Weight: ${n(w.weight, " kg")} | Resting HR: ${n(w.restingHR, " bpm")} | Avg Sleeping HR: ${n(w.avgSleepingHR, " bpm")}`,
59
59
  ` HRV: ${n(w.hrv)} | HRV SDNN: ${n(w.hrvSDNN)}`,
60
60
  ` SpO2: ${n(w.spO2, "%")} | VO2max: ${n(w.vo2max)} | Body Fat: ${n(w.bodyFat, "%")}`,
61
61
  "",
@@ -69,11 +69,13 @@ export function formatWellness(date, w) {
69
69
  "",
70
70
  "Nutrition:",
71
71
  ` Calories: ${n(w.kcalConsumed, " kcal")} | Steps: ${n(w.steps)}`,
72
- w.comments ? `\nComments: ${w.comments}` : "",
73
- ]
74
- .filter((l) => l !== undefined)
75
- .join("\n")
76
- .trimEnd();
72
+ ];
73
+ if (w.sportInfo?.length) {
74
+ lines.push("", "Sport Fitness (eFTP / W' / pMax):", ...w.sportInfo.map((s) => ` ${s.type ?? "Unknown"}: ${n(s.eftp, " W")} / ${n(s.wPrime, " J")} / ${n(s.pMax, " W")}`));
75
+ }
76
+ if (w.comments)
77
+ lines.push("", `Comments: ${w.comments}`);
78
+ return lines.join("\n").trimEnd();
77
79
  }
78
80
  export function formatEvent(e) {
79
81
  const category = e.category ?? "";
@@ -88,14 +90,19 @@ export function formatEvent(e) {
88
90
  const dateLabel = e.end_date_local && e.end_date_local !== e.start_date_local
89
91
  ? `${start} to ${e.end_date_local}`
90
92
  : start;
91
- return [
93
+ const lines = [
92
94
  `**${e.name ?? "Unnamed"}** (${e.id})`,
93
95
  `Type: ${type} | Date: ${dateLabel}`,
94
96
  e.category ? `Category: ${e.category}` : "",
95
- e.description ? `Description: ${e.description}` : "",
96
- ]
97
- .filter(Boolean)
98
- .join("\n");
97
+ ];
98
+ if (e.type || e.moving_time != null || e.icu_training_load != null || e.icu_intensity != null) {
99
+ lines.push(`Sport: ${n(e.type)} | Planned Duration: ${e.moving_time != null ? fmtDuration(e.moving_time) : "N/A"} | Planned Load: ${n(e.icu_training_load)} | Intensity: ${n(e.icu_intensity)}`);
100
+ }
101
+ if (e.paired_activity_id)
102
+ lines.push(`Completed as: ${e.paired_activity_id}`);
103
+ if (e.description)
104
+ lines.push(`Description: ${e.description}`);
105
+ return lines.filter(Boolean).join("\n");
99
106
  }
100
107
  export function formatMessage(m) {
101
108
  const when = m.created ? new Date(m.created).toLocaleString() : "Unknown time";
@@ -146,6 +153,13 @@ export function formatSportZones(s) {
146
153
  const range = z.to != null ? `${z.from}-${z.to} m/s` : `${z.from}+ m/s`;
147
154
  lines.push(` ${z.name}: ${range}`);
148
155
  }
156
+ lines.push("");
157
+ }
158
+ if (s.w_prime != null || s.sweet_spot_min != null) {
159
+ lines.push(`Power Model: W' ${n(s.w_prime, " J")} | Sweet Spot ${n(s.sweet_spot_min, "%")}-${n(s.sweet_spot_max, "%")}`);
160
+ }
161
+ if (s.best_effort_distances?.length) {
162
+ lines.push(`Best Effort Distances: ${s.best_effort_distances.join(", ")} m`);
149
163
  }
150
164
  return lines.join("\n").trimEnd();
151
165
  }
@@ -160,10 +174,13 @@ export function formatIntervalRow(interval, idx) {
160
174
  const avgHr = interval["average_heartrate"] ?? 0;
161
175
  const maxHr = interval["max_heartrate"] ?? 0;
162
176
  const tl = interval["training_load"] ?? 0;
177
+ const intensity = interval["intensity"];
178
+ const vi = interval["w5s_variability"];
179
+ const decoupling = interval["decoupling"];
163
180
  return [
164
181
  `[${idx}] ${label} (${type})`,
165
182
  ` Duration: ${elapsed}s | Distance: ${distance}m`,
166
- ` Power: avg ${avgW}W, w.avg ${wAvg}W, max ${maxW}W | TL: ${tl}`,
167
- ` HR: avg ${avgHr}, max ${maxHr} bpm`,
183
+ ` Power: avg ${avgW}W, w.avg ${wAvg}W, max ${maxW}W | TL: ${tl} | Intensity: ${n(intensity)} | VI: ${n(vi)}`,
184
+ ` HR: avg ${avgHr}, max ${maxHr} bpm | Decoupling: ${n(decoupling, "%")}`,
168
185
  ].join("\n");
169
186
  }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ 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, getActivityMessages, addActivityMessage, } from "./client.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, deleteActivityMessage, updateActivityMessage, } from "./client.js";
8
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 ?? "";
@@ -309,6 +309,35 @@ const TOOLS = [
309
309
  },
310
310
  },
311
311
  },
312
+ {
313
+ name: "update_activity_message",
314
+ description: "Edit a comment/note on an activity. Intervals.icu has no API to edit message content in place, so this deletes the old message and posts a new one with the updated text — the old message will show as \"(deleted)\" and the edited one will appear as a new message at the current time. Use get_activity_messages to find the message_id.",
315
+ inputSchema: {
316
+ type: "object",
317
+ required: ["activity_id", "message_id", "content"],
318
+ properties: {
319
+ activity_id: { type: "string", description: "The activity ID" },
320
+ message_id: { type: "number", description: "ID of the message to edit (from get_activity_messages)" },
321
+ content: { type: "string", description: "New comment text" },
322
+ athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
323
+ api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
324
+ },
325
+ },
326
+ },
327
+ {
328
+ name: "delete_activity_message",
329
+ description: "Delete a comment/note from an activity's chat thread. The message is soft-deleted (shows as \"(deleted)\") rather than removed entirely. Use get_activity_messages to find the message_id.",
330
+ inputSchema: {
331
+ type: "object",
332
+ required: ["activity_id", "message_id"],
333
+ properties: {
334
+ activity_id: { type: "string", description: "The activity ID" },
335
+ message_id: { type: "number", description: "ID of the message to delete (from get_activity_messages)" },
336
+ athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
337
+ api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
338
+ },
339
+ },
340
+ },
312
341
  {
313
342
  name: "list_workouts",
314
343
  description: "List all workouts in the athlete's workout library.",
@@ -608,6 +637,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
608
637
  `ATL (Fatigue): ${summary.fatigue ?? "N/A"}`,
609
638
  `TSB (Form): ${summary.form ?? "N/A"}`,
610
639
  `Ramp Rate: ${summary.rampRate ?? "N/A"}`,
640
+ "",
641
+ "## Last 7 Days",
642
+ "",
643
+ `Activities: ${summary.count ?? "N/A"} | Training Load: ${summary.training_load ?? "N/A"}`,
644
+ `Moving Time: ${summary.moving_time != null ? `${(summary.moving_time / 3600).toFixed(1)}h` : "N/A"} | Distance: ${summary.distance != null ? `${(summary.distance / 1000).toFixed(1)} km` : "N/A"} | Calories: ${summary.calories ?? "N/A"}`,
611
645
  ].join("\n");
612
646
  return { content: [{ type: "text", text }] };
613
647
  }
@@ -667,12 +701,35 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
667
701
  await addActivityMessage(config, activityId, content);
668
702
  return { content: [{ type: "text", text: `Comment added to activity ${activityId}.` }] };
669
703
  }
704
+ case "update_activity_message": {
705
+ const activityId = z.string().parse(args["activity_id"]);
706
+ const messageId = z.number().parse(args["message_id"]);
707
+ const content = z.string().parse(args["content"]);
708
+ const config = getConfig(args);
709
+ await updateActivityMessage(config, activityId, messageId, content);
710
+ return { content: [{ type: "text", text: `Message ${messageId} on activity ${activityId} replaced with: "${content}"` }] };
711
+ }
712
+ case "delete_activity_message": {
713
+ const activityId = z.string().parse(args["activity_id"]);
714
+ const messageId = z.number().parse(args["message_id"]);
715
+ const config = getConfig(args);
716
+ await deleteActivityMessage(config, activityId, messageId);
717
+ return { content: [{ type: "text", text: `Message ${messageId} deleted from activity ${activityId}.` }] };
718
+ }
670
719
  case "list_workouts": {
671
720
  const config = getConfig(args);
672
721
  const workouts = await listWorkouts(config);
673
722
  if (!workouts.length)
674
723
  return { content: [{ type: "text", text: "No workouts in library." }] };
675
- const text = workouts.map(w => [`**${w.name ?? "Unnamed"}** (${w.id})`, w.type ? `Type: ${w.type}` : "", w.description ? `${w.description}` : ""].filter(Boolean).join("\n")).join("\n\n---\n\n");
724
+ const text = workouts.map(w => {
725
+ const duration = w.moving_time != null ? `${Math.round(w.moving_time / 60)}min` : "N/A";
726
+ const kj = w.joules != null ? `${Math.round(w.joules / 1000)} kJ` : "N/A";
727
+ return [
728
+ `**${w.name ?? "Unnamed"}** (${w.id})`,
729
+ w.type ? `Type: ${w.type} | Duration: ${duration} | Load: ${w.icu_training_load ?? "N/A"} | Energy: ${kj}` : "",
730
+ w.description ? `${w.description}` : "",
731
+ ].filter(Boolean).join("\n");
732
+ }).join("\n\n---\n\n");
676
733
  return { content: [{ type: "text", text }] };
677
734
  }
678
735
  case "create_workout": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "intervals-icu-mcp-server",
3
- "version": "0.2.1",
4
- "description": "MCP server for Intervals.icu — 24 tools for activities, wellness, calendar events, workouts and more",
3
+ "version": "0.3.0",
4
+ "description": "MCP server for Intervals.icu — 26 tools for activities, wellness, calendar events, workouts and more",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "intervals-icu-mcp": "dist/index.js"