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 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: ${n(a.total_elevation_gain, " m")}`,
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.9",
4
- "description": "MCP server for Intervals.icu — 20 tools for activities, wellness, calendar events, workouts and more",
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"