intervals-icu-mcp-server 0.1.0 → 0.1.2
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/README.md +67 -36
- package/dist/client.d.ts +38 -0
- package/dist/client.js +25 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +76 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,49 +1,33 @@
|
|
|
1
|
-
# intervals-mcp
|
|
1
|
+
# intervals-icu-mcp-server
|
|
2
2
|
|
|
3
3
|
MCP server for [Intervals.icu](https://intervals.icu) — connects Claude / Claude Code to your training data.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
| Tool | Description |
|
|
8
|
-
|------|-------------|
|
|
9
|
-
| `get_activities` | List activities with power, HR, training load, etc. |
|
|
10
|
-
| `get_activity_details` | Full metrics for a specific activity |
|
|
11
|
-
| `get_activity_intervals` | Per-interval breakdown (power, HR, speed, elevation) |
|
|
12
|
-
| `get_wellness_data` | Weight, HRV, sleep, CTL/ATL, subjective scores |
|
|
13
|
-
| `get_events` | Calendar events (workouts, races) |
|
|
14
|
-
| `get_event_by_id` | Full details for a specific event |
|
|
15
|
-
|
|
16
|
-
## Setup
|
|
17
|
-
|
|
18
|
-
### 1. Install & build
|
|
5
|
+
## Installation
|
|
19
6
|
|
|
20
7
|
```bash
|
|
21
|
-
|
|
22
|
-
npm run build
|
|
8
|
+
npx -y intervals-icu-mcp-server
|
|
23
9
|
```
|
|
24
10
|
|
|
25
|
-
|
|
11
|
+
Or install globally:
|
|
26
12
|
|
|
27
13
|
```bash
|
|
28
|
-
|
|
29
|
-
# Edit .env and fill in your API_KEY and ATHLETE_ID
|
|
14
|
+
npm install -g intervals-icu-mcp-server
|
|
30
15
|
```
|
|
31
16
|
|
|
32
|
-
|
|
33
|
-
- **ATHLETE_ID**: The `i12345` part of your intervals.icu profile URL
|
|
17
|
+
## Setup
|
|
34
18
|
|
|
35
|
-
###
|
|
19
|
+
### Claude Desktop
|
|
36
20
|
|
|
37
|
-
Add
|
|
21
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
38
22
|
|
|
39
23
|
```json
|
|
40
24
|
{
|
|
41
25
|
"mcpServers": {
|
|
42
|
-
"intervals": {
|
|
43
|
-
"command": "
|
|
44
|
-
"args": ["
|
|
26
|
+
"intervals-icu": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "intervals-icu-mcp-server"],
|
|
45
29
|
"env": {
|
|
46
|
-
"API_KEY": "
|
|
30
|
+
"API_KEY": "your_api_key",
|
|
47
31
|
"ATHLETE_ID": "i12345"
|
|
48
32
|
}
|
|
49
33
|
}
|
|
@@ -51,20 +35,18 @@ Add this to your `~/.claude/settings.json` (or project `.claude/settings.json`):
|
|
|
51
35
|
}
|
|
52
36
|
```
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### 4. Add to Claude Desktop
|
|
38
|
+
### Claude Code
|
|
57
39
|
|
|
58
|
-
|
|
40
|
+
Add to `~/.claude/settings.json`:
|
|
59
41
|
|
|
60
42
|
```json
|
|
61
43
|
{
|
|
62
44
|
"mcpServers": {
|
|
63
|
-
"intervals": {
|
|
64
|
-
"command": "
|
|
65
|
-
"args": ["
|
|
45
|
+
"intervals-icu": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "intervals-icu-mcp-server"],
|
|
66
48
|
"env": {
|
|
67
|
-
"API_KEY": "
|
|
49
|
+
"API_KEY": "your_api_key",
|
|
68
50
|
"ATHLETE_ID": "i12345"
|
|
69
51
|
}
|
|
70
52
|
}
|
|
@@ -72,9 +54,58 @@ In `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
|
72
54
|
}
|
|
73
55
|
```
|
|
74
56
|
|
|
57
|
+
- **API_KEY**: Settings → API → Generate key on intervals.icu
|
|
58
|
+
- **ATHLETE_ID**: The `i12345` part of your intervals.icu profile URL
|
|
59
|
+
|
|
60
|
+
## Tools
|
|
61
|
+
|
|
62
|
+
### Activities
|
|
63
|
+
| Tool | Description |
|
|
64
|
+
|------|-------------|
|
|
65
|
+
| `get_activities` | List activities with power, HR, training load, etc. |
|
|
66
|
+
| `get_activity_details` | Full metrics for a specific activity |
|
|
67
|
+
| `get_activity_intervals` | Per-interval breakdown (power, HR, speed, elevation) |
|
|
68
|
+
| `get_activity_streams` | Raw per-second data streams (watts, HR, cadence, speed, altitude) |
|
|
69
|
+
| `get_activity_power_curves` | Best power for all durations (5s, 1min, 5min, 20min, etc.) |
|
|
70
|
+
|
|
71
|
+
### Wellness & Fitness
|
|
72
|
+
| Tool | Description |
|
|
73
|
+
|------|-------------|
|
|
74
|
+
| `get_wellness_data` | Weight, HRV, sleep, CTL/ATL, subjective scores for a date range |
|
|
75
|
+
| `get_wellness_day` | Wellness data for a single date |
|
|
76
|
+
| `update_wellness` | Log weight, HRV, sleep, mood, soreness, etc. |
|
|
77
|
+
|
|
78
|
+
### Athlete
|
|
79
|
+
| Tool | Description |
|
|
80
|
+
|------|-------------|
|
|
81
|
+
| `get_athlete_profile` | FTP, LTHR, weight, VO2max, resting HR |
|
|
82
|
+
| `get_athlete_zones` | Power, HR and pace training zones |
|
|
83
|
+
| `get_athlete_summary` | Current CTL, ATL, TSB and ramp rate snapshot |
|
|
84
|
+
|
|
85
|
+
### Calendar Events
|
|
86
|
+
| Tool | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `get_events` | Calendar events (workouts, races) for a date range |
|
|
89
|
+
| `get_event_by_id` | Full details for a specific event |
|
|
90
|
+
| `create_event` | Create a new event (workout, note, race) |
|
|
91
|
+
| `update_event` | Update event fields or move it to a new date |
|
|
92
|
+
| `delete_event` | Delete an event |
|
|
93
|
+
| `bulk_create_events` | Create multiple events at once |
|
|
94
|
+
| `bulk_delete_events` | Delete multiple events at once |
|
|
95
|
+
|
|
96
|
+
### Workout Library
|
|
97
|
+
| Tool | Description |
|
|
98
|
+
|------|-------------|
|
|
99
|
+
| `list_workouts` | List all workouts in the library |
|
|
100
|
+
| `create_workout` | Save a new structured workout to the library |
|
|
101
|
+
|
|
75
102
|
## Development
|
|
76
103
|
|
|
77
104
|
```bash
|
|
105
|
+
git clone https://github.com/SergeyPirogov/intervals-mcp.git
|
|
106
|
+
cd intervals-mcp
|
|
107
|
+
npm install
|
|
108
|
+
npm run build
|
|
78
109
|
npm run dev # watch mode
|
|
79
110
|
npm run inspector # MCP inspector UI
|
|
80
111
|
```
|
package/dist/client.d.ts
CHANGED
|
@@ -202,6 +202,7 @@ export interface EventInput {
|
|
|
202
202
|
race?: boolean;
|
|
203
203
|
distance?: number;
|
|
204
204
|
sub_type?: string;
|
|
205
|
+
workout_doc?: unknown;
|
|
205
206
|
}
|
|
206
207
|
export declare const AthleteSchema: z.ZodObject<{
|
|
207
208
|
id: z.ZodOptional<z.ZodString>;
|
|
@@ -346,6 +347,7 @@ export declare function getEvents(config: ClientConfig, params: {
|
|
|
346
347
|
endDate: string;
|
|
347
348
|
}): Promise<Event[]>;
|
|
348
349
|
export declare function getEventById(config: ClientConfig, eventId: string): Promise<Event>;
|
|
350
|
+
export declare function getNotesByDate(config: ClientConfig, date: string): Promise<Event[]>;
|
|
349
351
|
export declare function createEvent(config: ClientConfig, input: EventInput): Promise<Event>;
|
|
350
352
|
export declare function updateEvent(config: ClientConfig, eventId: string, input: EventInput): Promise<Event>;
|
|
351
353
|
export declare function deleteEvent(config: ClientConfig, eventId: string): Promise<void>;
|
|
@@ -360,3 +362,39 @@ export declare function listWorkouts(config: ClientConfig): Promise<Workout[]>;
|
|
|
360
362
|
export declare function createWorkout(config: ClientConfig, input: WorkoutInput): Promise<Workout>;
|
|
361
363
|
export declare function bulkCreateEvents(config: ClientConfig, events: EventInput[]): Promise<Event[]>;
|
|
362
364
|
export declare function bulkDeleteEvents(config: ClientConfig, eventIds: (string | number)[]): Promise<void>;
|
|
365
|
+
export declare const AthleteFitnessSchema: z.ZodObject<{
|
|
366
|
+
date: z.ZodOptional<z.ZodString>;
|
|
367
|
+
fitness: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
368
|
+
fatigue: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
369
|
+
form: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
370
|
+
rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
371
|
+
training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
372
|
+
eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
373
|
+
eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
374
|
+
weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
375
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
376
|
+
date: z.ZodOptional<z.ZodString>;
|
|
377
|
+
fitness: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
378
|
+
fatigue: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
379
|
+
form: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
380
|
+
rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
381
|
+
training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
382
|
+
eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
383
|
+
eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
384
|
+
weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
385
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
386
|
+
date: z.ZodOptional<z.ZodString>;
|
|
387
|
+
fitness: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
388
|
+
fatigue: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
389
|
+
form: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
390
|
+
rampRate: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
391
|
+
training_load: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
392
|
+
eftp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
393
|
+
eftpPerKg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
394
|
+
weight: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
395
|
+
}, z.ZodTypeAny, "passthrough">>;
|
|
396
|
+
export type AthleteFitness = z.infer<typeof AthleteFitnessSchema>;
|
|
397
|
+
export declare function getAthleteFitness(config: ClientConfig, params: {
|
|
398
|
+
startDate: string;
|
|
399
|
+
endDate: string;
|
|
400
|
+
}): Promise<AthleteFitness[]>;
|
package/dist/client.js
CHANGED
|
@@ -155,9 +155,17 @@ export async function getEvents(config, params) {
|
|
|
155
155
|
return z.array(EventSchema).parse(data);
|
|
156
156
|
}
|
|
157
157
|
export async function getEventById(config, eventId) {
|
|
158
|
-
const data = await request(`/athlete/${config.athleteId}/
|
|
158
|
+
const data = await request(`/athlete/${config.athleteId}/events/${eventId}`, config);
|
|
159
159
|
return EventSchema.parse(data);
|
|
160
160
|
}
|
|
161
|
+
export async function getNotesByDate(config, date) {
|
|
162
|
+
const data = await request(`/athlete/${config.athleteId}/events`, config, { oldest: date, newest: date });
|
|
163
|
+
const events = z.array(EventSchema).parse(data);
|
|
164
|
+
return events.filter((e) => {
|
|
165
|
+
const raw = e;
|
|
166
|
+
return raw["category"] === "NOTE" || (!e.race && !e.workout);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
161
169
|
async function mutate(method, path, config, body) {
|
|
162
170
|
const url = `${BASE_URL}${path}`;
|
|
163
171
|
const res = await fetch(url, {
|
|
@@ -234,3 +242,19 @@ export async function bulkCreateEvents(config, events) {
|
|
|
234
242
|
export async function bulkDeleteEvents(config, eventIds) {
|
|
235
243
|
await mutate("POST", `/athlete/${config.athleteId}/events/bulk-delete`, config, eventIds);
|
|
236
244
|
}
|
|
245
|
+
export const AthleteFitnessSchema = z.object({
|
|
246
|
+
date: z.string().optional(),
|
|
247
|
+
fitness: z.number().nullable().optional(),
|
|
248
|
+
fatigue: z.number().nullable().optional(),
|
|
249
|
+
form: z.number().nullable().optional(),
|
|
250
|
+
rampRate: z.number().nullable().optional(),
|
|
251
|
+
training_load: z.number().nullable().optional(),
|
|
252
|
+
eftp: z.number().nullable().optional(),
|
|
253
|
+
eftpPerKg: z.number().nullable().optional(),
|
|
254
|
+
weight: z.number().nullable().optional(),
|
|
255
|
+
}).passthrough();
|
|
256
|
+
export async function getAthleteFitness(config, params) {
|
|
257
|
+
const data = await request(`/athlete/${config.athleteId}/athlete-summary.json`, config, { oldest: params.startDate, newest: params.endDate, athleteId: config.athleteId });
|
|
258
|
+
const all = z.array(AthleteFitnessSchema).parse(data);
|
|
259
|
+
return all.filter((e) => e["athlete_id"] === config.athleteId);
|
|
260
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import "dotenv/config";
|
|
2
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
5
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
6
|
import { z } from "zod";
|
|
6
|
-
import { getActivities, getActivityDetails, getActivityIntervals, getWellness, getEvents, getEventById, createEvent, updateEvent, deleteEvent, getAthleteProfile, getAthleteZones, getAthleteSummary, getWellnessDay, updateWellness, getActivityStreams, getActivityPowerCurves, listWorkouts, createWorkout, bulkCreateEvents, bulkDeleteEvents, } 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, } from "./client.js";
|
|
7
8
|
import { formatActivity, formatWellness, formatEvent, formatIntervalRow, } from "./format.js";
|
|
8
9
|
function getConfig(args) {
|
|
9
10
|
const apiKey = args["api_key"] ?? process.env.API_KEY ?? "";
|
|
@@ -125,6 +126,7 @@ const TOOLS = [
|
|
|
125
126
|
race: { type: "boolean", description: "Mark as a race event (default: false)" },
|
|
126
127
|
distance: { type: "number", description: "Distance in meters (e.g. 100000 for 100km)" },
|
|
127
128
|
sub_type: { type: "string", description: "Race category/sub-type (e.g. A, B, C)" },
|
|
129
|
+
workout_doc: { type: "object", description: "Structured workout definition (Intervals.icu workout_doc format) to attach steps/intervals to this event" },
|
|
128
130
|
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
129
131
|
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
130
132
|
},
|
|
@@ -146,6 +148,7 @@ const TOOLS = [
|
|
|
146
148
|
race: { type: "boolean", description: "Toggle race flag" },
|
|
147
149
|
distance: { type: "number", description: "Distance in meters (e.g. 100000 for 100km)" },
|
|
148
150
|
sub_type: { type: "string", description: "Race category/sub-type (e.g. A, B, C)" },
|
|
151
|
+
workout_doc: { type: "object", description: "New structured workout definition (Intervals.icu workout_doc format)" },
|
|
149
152
|
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
150
153
|
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
151
154
|
},
|
|
@@ -298,6 +301,32 @@ const TOOLS = [
|
|
|
298
301
|
},
|
|
299
302
|
},
|
|
300
303
|
},
|
|
304
|
+
{
|
|
305
|
+
name: "get_athlete_fitness",
|
|
306
|
+
description: "Get Fitness (CTL), Fatigue (ATL), Form (TSB), ramp rate and eFTP from the Fitness chart for a date range. Matches what you see on the Intervals.icu Fitness page.",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
start_date: { type: "string", description: "Start date YYYY-MM-DD (default: 30 days ago)" },
|
|
311
|
+
end_date: { type: "string", description: "End date YYYY-MM-DD (default: today)" },
|
|
312
|
+
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
313
|
+
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: "get_note",
|
|
319
|
+
description: "Get a specific note/event by ID, or list all notes for a given date.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
event_id: { type: "string", description: "Event/note ID to fetch by ID" },
|
|
324
|
+
date: { type: "string", description: "Date YYYY-MM-DD to list all notes for that day" },
|
|
325
|
+
athlete_id: { type: "string", description: "Athlete ID (defaults to ATHLETE_ID env var)" },
|
|
326
|
+
api_key: { type: "string", description: "API key (defaults to API_KEY env var)" },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
301
330
|
{
|
|
302
331
|
name: "bulk_create_events",
|
|
303
332
|
description: "Create multiple calendar events at once.",
|
|
@@ -317,6 +346,7 @@ const TOOLS = [
|
|
|
317
346
|
category: { type: "string" },
|
|
318
347
|
type: { type: "string" },
|
|
319
348
|
race: { type: "boolean" },
|
|
349
|
+
workout_doc: { type: "object", description: "Structured workout definition (Intervals.icu workout_doc format)" },
|
|
320
350
|
},
|
|
321
351
|
},
|
|
322
352
|
},
|
|
@@ -432,6 +462,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
432
462
|
const event = await getEventById(config, eventId);
|
|
433
463
|
return { content: [{ type: "text", text: formatEvent(event) }] };
|
|
434
464
|
}
|
|
465
|
+
case "get_athlete_fitness": {
|
|
466
|
+
const config = getConfig(args);
|
|
467
|
+
const startDate = args["start_date"] ?? daysAgo(30);
|
|
468
|
+
const endDate = args["end_date"] ?? today();
|
|
469
|
+
const entries = await getAthleteFitness(config, { startDate, endDate });
|
|
470
|
+
if (!entries.length) {
|
|
471
|
+
return { content: [{ type: "text", text: "No fitness data found for the specified date range." }] };
|
|
472
|
+
}
|
|
473
|
+
const lines = entries.map((e) => {
|
|
474
|
+
const ctl = e.fitness != null ? e.fitness.toFixed(1) : "N/A";
|
|
475
|
+
const atl = e.fatigue != null ? e.fatigue.toFixed(1) : "N/A";
|
|
476
|
+
const tsb = e.form != null ? e.form.toFixed(1) : "N/A";
|
|
477
|
+
const ramp = e.rampRate != null ? e.rampRate.toFixed(1) : "N/A";
|
|
478
|
+
const eftp = e.eftp != null ? `${e.eftp.toFixed(0)}W` : "N/A";
|
|
479
|
+
const eftpKg = e.eftpPerKg != null ? `${e.eftpPerKg.toFixed(2)} W/kg` : "N/A";
|
|
480
|
+
const tl = e.training_load != null ? e.training_load.toFixed(0) : "N/A";
|
|
481
|
+
return [
|
|
482
|
+
`**${e.date ?? "N/A"}**`,
|
|
483
|
+
` CTL (Fitness): ${ctl} | ATL (Fatigue): ${atl} | TSB (Form): ${tsb}`,
|
|
484
|
+
` Ramp Rate: ${ramp} | eFTP: ${eftp} (${eftpKg}) | TL: ${tl}`,
|
|
485
|
+
].join("\n");
|
|
486
|
+
});
|
|
487
|
+
return { content: [{ type: "text", text: lines.join("\n\n") }] };
|
|
488
|
+
}
|
|
489
|
+
case "get_note": {
|
|
490
|
+
const config = getConfig(args);
|
|
491
|
+
const eventId = args["event_id"];
|
|
492
|
+
const date = args["date"];
|
|
493
|
+
if (eventId) {
|
|
494
|
+
const event = await getEventById(config, eventId);
|
|
495
|
+
return { content: [{ type: "text", text: formatEvent(event) }] };
|
|
496
|
+
}
|
|
497
|
+
else if (date) {
|
|
498
|
+
const notes = await getNotesByDate(config, date);
|
|
499
|
+
if (!notes.length)
|
|
500
|
+
return { content: [{ type: "text", text: `No notes found for ${date}.` }] };
|
|
501
|
+
const text = notes.map(formatEvent).join("\n\n---\n\n");
|
|
502
|
+
return { content: [{ type: "text", text }] };
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
return { content: [{ type: "text", text: "Provide either event_id or date." }], isError: true };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
435
508
|
case "create_event": {
|
|
436
509
|
const config = getConfig(args);
|
|
437
510
|
const event = await createEvent(config, {
|
|
@@ -443,6 +516,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
443
516
|
race: args["race"],
|
|
444
517
|
distance: args["distance"],
|
|
445
518
|
sub_type: args["sub_type"],
|
|
519
|
+
workout_doc: args["workout_doc"],
|
|
446
520
|
});
|
|
447
521
|
return { content: [{ type: "text", text: `Created event:\n\n${formatEvent(event)}` }] };
|
|
448
522
|
}
|
|
@@ -450,7 +524,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
450
524
|
const eventId = z.string().parse(args["event_id"]);
|
|
451
525
|
const config = getConfig(args);
|
|
452
526
|
const input = {};
|
|
453
|
-
for (const key of ["name", "description", "start_date_local", "category", "type", "race", "distance", "sub_type"]) {
|
|
527
|
+
for (const key of ["name", "description", "start_date_local", "category", "type", "race", "distance", "sub_type", "workout_doc"]) {
|
|
454
528
|
if (args[key] !== undefined)
|
|
455
529
|
input[key] = args[key];
|
|
456
530
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intervals-icu-mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP server for Intervals.icu — 20 tools for activities, wellness, calendar events, workouts and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"intervals-icu-mcp": "
|
|
7
|
+
"intervals-icu-mcp": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "tsc",
|
|
10
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
11
11
|
"dev": "tsc --watch",
|
|
12
12
|
"start": "node dist/index.js",
|
|
13
13
|
"inspector": "npx @modelcontextprotocol/inspector node dist/index.js",
|