garmin-personal-api-mcp 1.0.3 → 1.1.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/README.md +2 -2
- package/dist/format.js +9 -2
- package/dist/format.js.map +1 -1
- package/dist/tools.js +1 -1
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Dates can be `YYYY-MM-DD`, `today`, `yesterday` or `N days ago`.
|
|
|
46
46
|
|
|
47
47
|
| Tool | What it gives Claude |
|
|
48
48
|
|---|---|
|
|
49
|
-
| `garmin_list_activities` | Your activities with distance, time, pace and
|
|
49
|
+
| `garmin_list_activities` | Your activities with distance, time, pace, heart rate, training effect and Garmin's training load. Filter by date and type |
|
|
50
50
|
| `garmin_get_activity_splits` | Downloads the original `.FIT` file and computes **exact** km (or mile, or 400 m) splits, interpolated at each boundary with pauses removed, plus per-split HR, cadence and climb, device laps, and a pacing verdict (even, negative split, faded) |
|
|
51
51
|
| `garmin_get_performance_metrics` | Your **VO2 max** as Garmin Connect shows it, every VO2 max reading in a window, and per-activity aerobic/anaerobic training effect, recovery time and performance condition, decoded from the activity files |
|
|
52
52
|
| `garmin_get_activity` | Garmin's own summary for one activity |
|
|
@@ -70,7 +70,7 @@ Dates can be `YYYY-MM-DD`, `today`, `yesterday` or `N days ago`.
|
|
|
70
70
|
| Tool | Try asking |
|
|
71
71
|
|---|---|
|
|
72
72
|
| `garmin_get_profile` | "What are my heart-rate zones?" |
|
|
73
|
-
| `garmin_list_activities` | "
|
|
73
|
+
| `garmin_list_activities` | "What was my training load this week, activity by activity?" |
|
|
74
74
|
| `garmin_get_performance_metrics` | "What's my VO2 max, and how has it moved this month?" |
|
|
75
75
|
| `garmin_get_activity` | "What was the training effect of my last ride?" |
|
|
76
76
|
| `garmin_get_activity_splits` | "How was my pacing on yesterday's run? Did I fade?" |
|
package/dist/format.js
CHANGED
|
@@ -54,6 +54,8 @@ export const textResult = (text) => ({ content: [{ type: "text", text }] });
|
|
|
54
54
|
const g = (o, k) => o[k];
|
|
55
55
|
const n = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
|
|
56
56
|
const str = (v) => (typeof v === "string" ? v : undefined);
|
|
57
|
+
const round1 = (v) => (v === undefined ? undefined : Math.round(v * 10) / 10);
|
|
58
|
+
const roundInt = (v) => (v === undefined ? undefined : Math.round(v));
|
|
57
59
|
export function summarizeActivity(raw) {
|
|
58
60
|
if (!isObj(raw))
|
|
59
61
|
return undefined;
|
|
@@ -81,16 +83,21 @@ export function summarizeActivity(raw) {
|
|
|
81
83
|
elevation_gain_m: n(g(summary, "elevationGain")),
|
|
82
84
|
calories: n(g(summary, "calories")),
|
|
83
85
|
avg_cadence: n(g(summary, "averageRunningCadenceInStepsPerMinute")) ?? n(g(summary, "averageRunCadence")),
|
|
84
|
-
training_effect: n(g(summary, "aerobicTrainingEffect")) ?? n(g(summary, "trainingEffect")),
|
|
86
|
+
training_effect: round1(n(g(summary, "aerobicTrainingEffect")) ?? n(g(summary, "trainingEffect"))),
|
|
87
|
+
anaerobic_training_effect: round1(n(g(summary, "anaerobicTrainingEffect"))),
|
|
88
|
+
training_effect_label: str(g(summary, "trainingEffectLabel"))?.toLowerCase().replace(/_/g, " "),
|
|
89
|
+
training_load: roundInt(n(g(summary, "activityTrainingLoad"))),
|
|
85
90
|
};
|
|
86
91
|
}
|
|
87
92
|
export function activityLine(a) {
|
|
88
93
|
const bits = [
|
|
89
|
-
a.distance_km
|
|
94
|
+
a.distance_km ? `${a.distance_km} km` : undefined,
|
|
90
95
|
a.duration,
|
|
91
96
|
a.avg_pace,
|
|
92
97
|
a.avg_hr ? `avg HR ${a.avg_hr}` : undefined,
|
|
93
98
|
a.elevation_gain_m ? `+${Math.round(a.elevation_gain_m)} m` : undefined,
|
|
99
|
+
a.training_effect !== undefined ? `TE ${a.training_effect}${a.training_effect_label ? ` (${a.training_effect_label})` : ""}` : undefined,
|
|
100
|
+
a.training_load !== undefined ? `load ${a.training_load}` : undefined,
|
|
94
101
|
].filter(Boolean);
|
|
95
102
|
return `- **${a.name ?? "Untitled"}** (${a.type ?? "activity"}, ${a.start_local ?? "?"}) · id ${a.activity_id}\n ${bits.join(" · ")}`;
|
|
96
103
|
}
|
package/dist/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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","sourcesContent":["import { CHARACTER_LIMIT } from \"./constants.js\";\nimport type { Json } from \"./api.js\";\nimport { fmtDuration, fmtPace } from \"./fit.js\";\n\ntype Obj = { [k: string]: Json };\nexport const isObj = (v: unknown): v is Obj => !!v && typeof v === \"object\" && !Array.isArray(v);\n\n/** Drops null/undefined/empty-string fields recursively; keeps zeros and false. */\nexport function compact(v: Json): Json {\n if (Array.isArray(v)) return v.map(compact);\n if (isObj(v)) {\n const out: Obj = {};\n for (const [k, x] of Object.entries(v)) {\n if (x === null || x === undefined || x === \"\") continue;\n out[k] = compact(x);\n }\n return out;\n }\n return v;\n}\n\n/**\n * Keeps a JSON payload under CHARACTER_LIMIT without cutting text mid-structure:\n * long arrays (usually per-minute samples) are evenly downsampled, halving until it fits.\n */\nexport function fitJson(value: Json, limit = CHARACTER_LIMIT): { value: Json; text: string; downsampled: boolean } {\n let v = compact(value);\n let text = JSON.stringify(v);\n if (text.length <= limit) return { value: v, text, downsampled: false };\n for (let cap = 512; cap >= 4; cap = Math.floor(cap / 2)) {\n v = downsample(compact(value), cap);\n text = JSON.stringify(v);\n if (text.length <= limit) return { value: v, text, downsampled: true };\n }\n throw new Error(\n \"The response is too large even after downsampling. Ask for a shorter date range, or use a summary tool such as garmin_get_daily_summaries.\",\n );\n}\n\nfunction downsample(v: Json, cap: number): Json {\n if (Array.isArray(v)) {\n const arr = v.length > cap ? Array.from({ length: cap }, (_, i) => v[Math.floor((i * v.length) / cap)]) : v;\n return arr.map((x) => downsample(x, cap));\n }\n if (isObj(v)) return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, downsample(x, cap)]));\n return v;\n}\n\nexport function jsonResult(value: Json, note?: string) {\n const { text, downsampled } = fitJson(value);\n const notes = [note, downsampled ? \"Long sample arrays were evenly downsampled to fit; ask for a single day for full resolution.\" : undefined]\n .filter(Boolean)\n .join(\" \");\n return { content: [{ type: \"text\" as const, text: notes ? `${notes}\\n\\n${text}` : text }] };\n}\n\nexport const textResult = (text: string) => ({ content: [{ type: \"text\" as const, text }] });\n\n// ---- Activity summaries (field names as returned by Garmin Connect's activity list) ----\n\nconst g = (o: Obj, k: string) => o[k];\nconst n = (v: Json | undefined) => (typeof v === \"number\" && Number.isFinite(v) ? v : undefined);\nconst str = (v: Json | undefined) => (typeof v === \"string\" ? v : undefined);\n\nexport interface ActivitySummary {\n activity_id: number | string;\n name?: string;\n type?: string;\n start_local?: string;\n distance_km?: number;\n duration?: string;\n moving_time?: string;\n avg_pace?: string;\n avg_hr?: number;\n max_hr?: number;\n elevation_gain_m?: number;\n calories?: number;\n avg_cadence?: number;\n training_effect?: number;\n}\n\nexport function summarizeActivity(raw: Json): ActivitySummary | undefined {\n if (!isObj(raw)) return undefined;\n const id = g(raw, \"activityId\");\n if (typeof id !== \"number\" && typeof id !== \"string\") return undefined;\n const summary = isObj(raw.summaryDTO) ? raw.summaryDTO : raw; // detail endpoint nests stats\n const type = isObj(raw.activityType) ? str(raw.activityType.typeKey) : isObj(raw.activityTypeDTO) ? str(raw.activityTypeDTO.typeKey) : undefined;\n const dist = n(g(summary, \"distance\"));\n const dur = n(g(summary, \"duration\"));\n const moving = n(g(summary, \"movingDuration\"));\n const speed = n(g(summary, \"averageSpeed\"));\n const isFoot = !!type && /run|walk|hik|trail|treadmill/.test(type);\n return {\n activity_id: id,\n name: str(raw.activityName),\n type,\n start_local: str(g(summary, \"startTimeLocal\")) ?? str(raw.startTimeLocal),\n distance_km: dist !== undefined ? Math.round(dist / 10) / 100 : undefined,\n duration: dur !== undefined ? fmtDuration(dur) : undefined,\n moving_time: moving !== undefined ? fmtDuration(moving) : undefined,\n avg_pace: isFoot && speed ? fmtPace(1000 / speed) : undefined,\n avg_hr: n(g(summary, \"averageHR\")),\n max_hr: n(g(summary, \"maxHR\")),\n elevation_gain_m: n(g(summary, \"elevationGain\")),\n calories: n(g(summary, \"calories\")),\n avg_cadence: n(g(summary, \"averageRunningCadenceInStepsPerMinute\")) ?? n(g(summary, \"averageRunCadence\")),\n training_effect: n(g(summary, \"aerobicTrainingEffect\")) ?? n(g(summary, \"trainingEffect\")),\n };\n}\n\nexport function activityLine(a: ActivitySummary): string {\n const bits = [\n a.distance_km !== undefined ? `${a.distance_km} km` : undefined,\n a.duration,\n a.avg_pace,\n a.avg_hr ? `avg HR ${a.avg_hr}` : undefined,\n a.elevation_gain_m ? `+${Math.round(a.elevation_gain_m)} m` : undefined,\n ].filter(Boolean);\n return `- **${a.name ?? \"Untitled\"}** (${a.type ?? \"activity\"}, ${a.start_local ?? \"?\"}) · id ${a.activity_id}\\n ${bits.join(\" · \")}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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","sourcesContent":["import { CHARACTER_LIMIT } from \"./constants.js\";\nimport type { Json } from \"./api.js\";\nimport { fmtDuration, fmtPace } from \"./fit.js\";\n\ntype Obj = { [k: string]: Json };\nexport const isObj = (v: unknown): v is Obj => !!v && typeof v === \"object\" && !Array.isArray(v);\n\n/** Drops null/undefined/empty-string fields recursively; keeps zeros and false. */\nexport function compact(v: Json): Json {\n if (Array.isArray(v)) return v.map(compact);\n if (isObj(v)) {\n const out: Obj = {};\n for (const [k, x] of Object.entries(v)) {\n if (x === null || x === undefined || x === \"\") continue;\n out[k] = compact(x);\n }\n return out;\n }\n return v;\n}\n\n/**\n * Keeps a JSON payload under CHARACTER_LIMIT without cutting text mid-structure:\n * long arrays (usually per-minute samples) are evenly downsampled, halving until it fits.\n */\nexport function fitJson(value: Json, limit = CHARACTER_LIMIT): { value: Json; text: string; downsampled: boolean } {\n let v = compact(value);\n let text = JSON.stringify(v);\n if (text.length <= limit) return { value: v, text, downsampled: false };\n for (let cap = 512; cap >= 4; cap = Math.floor(cap / 2)) {\n v = downsample(compact(value), cap);\n text = JSON.stringify(v);\n if (text.length <= limit) return { value: v, text, downsampled: true };\n }\n throw new Error(\n \"The response is too large even after downsampling. Ask for a shorter date range, or use a summary tool such as garmin_get_daily_summaries.\",\n );\n}\n\nfunction downsample(v: Json, cap: number): Json {\n if (Array.isArray(v)) {\n const arr = v.length > cap ? Array.from({ length: cap }, (_, i) => v[Math.floor((i * v.length) / cap)]) : v;\n return arr.map((x) => downsample(x, cap));\n }\n if (isObj(v)) return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, downsample(x, cap)]));\n return v;\n}\n\nexport function jsonResult(value: Json, note?: string) {\n const { text, downsampled } = fitJson(value);\n const notes = [note, downsampled ? \"Long sample arrays were evenly downsampled to fit; ask for a single day for full resolution.\" : undefined]\n .filter(Boolean)\n .join(\" \");\n return { content: [{ type: \"text\" as const, text: notes ? `${notes}\\n\\n${text}` : text }] };\n}\n\nexport const textResult = (text: string) => ({ content: [{ type: \"text\" as const, text }] });\n\n// ---- Activity summaries (field names as returned by Garmin Connect's activity list) ----\n\nconst g = (o: Obj, k: string) => o[k];\nconst n = (v: Json | undefined) => (typeof v === \"number\" && Number.isFinite(v) ? v : undefined);\nconst str = (v: Json | undefined) => (typeof v === \"string\" ? v : undefined);\nconst round1 = (v: number | undefined) => (v === undefined ? undefined : Math.round(v * 10) / 10);\nconst roundInt = (v: number | undefined) => (v === undefined ? undefined : Math.round(v));\n\nexport interface ActivitySummary {\n activity_id: number | string;\n name?: string;\n type?: string;\n start_local?: string;\n distance_km?: number;\n duration?: string;\n moving_time?: string;\n avg_pace?: string;\n avg_hr?: number;\n max_hr?: number;\n elevation_gain_m?: number;\n calories?: number;\n avg_cadence?: number;\n training_effect?: number;\n anaerobic_training_effect?: number;\n /** Garmin's label for the main benefit, e.g. \"lactate threshold\". */\n training_effect_label?: string;\n /** Garmin's per-activity training load (EPOC-based), as Garmin Connect shows it. */\n training_load?: number;\n}\n\nexport function summarizeActivity(raw: Json): ActivitySummary | undefined {\n if (!isObj(raw)) return undefined;\n const id = g(raw, \"activityId\");\n if (typeof id !== \"number\" && typeof id !== \"string\") return undefined;\n const summary = isObj(raw.summaryDTO) ? raw.summaryDTO : raw; // detail endpoint nests stats\n const type = isObj(raw.activityType) ? str(raw.activityType.typeKey) : isObj(raw.activityTypeDTO) ? str(raw.activityTypeDTO.typeKey) : undefined;\n const dist = n(g(summary, \"distance\"));\n const dur = n(g(summary, \"duration\"));\n const moving = n(g(summary, \"movingDuration\"));\n const speed = n(g(summary, \"averageSpeed\"));\n const isFoot = !!type && /run|walk|hik|trail|treadmill/.test(type);\n return {\n activity_id: id,\n name: str(raw.activityName),\n type,\n start_local: str(g(summary, \"startTimeLocal\")) ?? str(raw.startTimeLocal),\n distance_km: dist !== undefined ? Math.round(dist / 10) / 100 : undefined,\n duration: dur !== undefined ? fmtDuration(dur) : undefined,\n moving_time: moving !== undefined ? fmtDuration(moving) : undefined,\n avg_pace: isFoot && speed ? fmtPace(1000 / speed) : undefined,\n avg_hr: n(g(summary, \"averageHR\")),\n max_hr: n(g(summary, \"maxHR\")),\n elevation_gain_m: n(g(summary, \"elevationGain\")),\n calories: n(g(summary, \"calories\")),\n avg_cadence: n(g(summary, \"averageRunningCadenceInStepsPerMinute\")) ?? n(g(summary, \"averageRunCadence\")),\n training_effect: round1(n(g(summary, \"aerobicTrainingEffect\")) ?? n(g(summary, \"trainingEffect\"))),\n anaerobic_training_effect: round1(n(g(summary, \"anaerobicTrainingEffect\"))),\n training_effect_label: str(g(summary, \"trainingEffectLabel\"))?.toLowerCase().replace(/_/g, \" \"),\n training_load: roundInt(n(g(summary, \"activityTrainingLoad\"))),\n };\n}\n\nexport function activityLine(a: ActivitySummary): string {\n const bits = [\n a.distance_km ? `${a.distance_km} km` : undefined,\n a.duration,\n a.avg_pace,\n a.avg_hr ? `avg HR ${a.avg_hr}` : undefined,\n a.elevation_gain_m ? `+${Math.round(a.elevation_gain_m)} m` : undefined,\n a.training_effect !== undefined ? `TE ${a.training_effect}${a.training_effect_label ? ` (${a.training_effect_label})` : \"\"}` : undefined,\n a.training_load !== undefined ? `load ${a.training_load}` : undefined,\n ].filter(Boolean);\n return `- **${a.name ?? \"Untitled\"}** (${a.type ?? \"activity\"}, ${a.start_local ?? \"?\"}) · id ${a.activity_id}\\n ${bits.join(\" · \")}`;\n}\n"]}
|
package/dist/tools.js
CHANGED
|
@@ -32,7 +32,7 @@ export function registerTools(server, client) {
|
|
|
32
32
|
// ---------------- Activities ----------------
|
|
33
33
|
server.registerTool("garmin_list_activities", {
|
|
34
34
|
title: "List activities",
|
|
35
|
-
description: `List recorded activities (runs, gym sessions, rides…), newest first, with distance, time, pace and
|
|
35
|
+
description: `List recorded activities (runs, gym sessions, rides…), newest first, with distance, time, pace, heart rate, training effect and Garmin's training load.
|
|
36
36
|
|
|
37
37
|
Examples:
|
|
38
38
|
- "my runs this month" -> start_date="2026-10-01", activity_type="running"
|
package/dist/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport * as z from \"zod/v4\";\nimport {\n ALLOWED_PATH_PREFIXES,\n fetchRange,\n type GarminClient,\n type Json,\n type RangeEndpoint,\n resolveDate,\n resolveRange,\n} from \"./api.js\";\nimport { decodeFit, extractFit, fmtDuration, fmtPace, type PerformanceMetrics, performanceMetrics, summarizeFit } from \"./fit.js\";\nimport { activityLine, isObj, jsonResult, summarizeActivity, textResult } from \"./format.js\";\n\nconst READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true } as const;\n\nconst DateArg = z\n .string()\n .min(1)\n .describe('Date as YYYY-MM-DD, or \"today\", \"yesterday\", \"N days ago\" (local time)');\nconst StartArg = DateArg.describe('First day, inclusive: YYYY-MM-DD, \"today\", \"yesterday\" or \"N days ago\"');\nconst EndArg = z.string().optional().describe('Last day, inclusive. Defaults to today. Same formats as start_date');\nconst ActivityId = z\n .union([z.string().regex(/^\\d+$/), z.number().int().positive()])\n .transform(String)\n .describe(\"Numeric Garmin activity ID, from garmin_list_activities\");\nconst Format = z.enum([\"markdown\", \"json\"]).default(\"markdown\").describe(\"'markdown' for a readable summary, 'json' for every field\");\n\nexport function registerTools(server: McpServer, client: GarminClient): void {\n // ---------------- Profile ----------------\n server.registerTool(\n \"garmin_get_profile\",\n {\n title: \"Profile & heart-rate zones\",\n description:\n \"The user's Garmin profile settings (units, weight, height, birth year, lactate threshold where set) plus their heart-rate zones. Call this first when you need zones or units to interpret other data.\",\n inputSchema: z.strictObject({}),\n annotations: READ_ONLY,\n },\n async (_args, ctx) => {\n const s = ctx.mcpReq.signal;\n const [profile, zones] = await Promise.all([client.profileSettings(s), client.heartRateZones(s).catch((e: Error) => ({ error: e.message }))]);\n return jsonResult({ profile, heart_rate_zones: zones as Json });\n },\n );\n\n // ---------------- Activities ----------------\n server.registerTool(\n \"garmin_list_activities\",\n {\n title: \"List activities\",\n description: `List recorded activities (runs, gym sessions, rides…), newest first, with distance, time, pace and heart rate.\n\nExamples:\n - \"my runs this month\" -> start_date=\"2026-10-01\", activity_type=\"running\"\n - \"last 5 activities\" -> limit=5\nUse garmin_get_activity_splits on an activity_id for per-km splits and pacing.`,\n inputSchema: z.strictObject({\n start_date: z.string().optional().describe(\"Only activities on/after this day (YYYY-MM-DD, 'today', 'N days ago')\"),\n end_date: z.string().optional().describe(\"Only activities on/before this day\"),\n activity_type: z\n .string()\n .optional()\n .describe(\"Garmin type key, e.g. running, treadmill_running, trail_running, cycling, indoor_cycling, strength_training, walking\"),\n limit: z.number().int().min(1).max(100).default(20).describe(\"Max activities to return\"),\n offset: z.number().int().min(0).default(0).describe(\"Skip this many, for paging\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ start_date, end_date, activity_type, limit, offset, response_format }, ctx) => {\n const raw = await client.searchActivities(\n {\n start: offset,\n limit,\n startDate: start_date ? resolveDate(start_date) : undefined,\n endDate: end_date ? resolveDate(end_date) : undefined,\n activityType: activity_type,\n },\n ctx.mcpReq.signal,\n );\n const list = Array.isArray(raw) ? raw : isObj(raw) && Array.isArray(raw.activityList) ? raw.activityList : [];\n if (response_format === \"json\") return jsonResult({ offset, count: list.length, has_more: list.length === limit, activities: list });\n const items = list.map(summarizeActivity).filter((a) => a !== undefined);\n if (!items.length) return textResult(\"No activities match. Widen the date range or drop activity_type.\");\n const lines = [`# ${items.length} activities`, \"\", ...items.map(activityLine)];\n if (list.length === limit) lines.push(\"\", `More may exist: call again with offset=${offset + limit}.`);\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_get_activity\",\n {\n title: \"Activity details\",\n description:\n \"Garmin's computed summary for one activity: distance, durations, pace/speed, HR, cadence, elevation, training effect, calories. For per-km splits use garmin_get_activity_splits.\",\n inputSchema: z.strictObject({ activity_id: ActivityId, response_format: Format }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, response_format }, ctx) => {\n const raw = await client.activity(activity_id, ctx.mcpReq.signal);\n if (response_format === \"json\") return jsonResult(raw);\n const a = summarizeActivity(raw);\n if (!a) return jsonResult(raw, \"Could not summarise this activity; raw data follows.\");\n return textResult(\n [\n `# ${a.name ?? \"Activity\"} (${a.type ?? \"?\"})`,\n ...Object.entries(a)\n .filter(([k, v]) => v !== undefined && k !== \"name\" && k !== \"type\")\n .map(([k, v]) => `- ${k.replace(/_/g, \" \")}: ${v}`),\n \"\",\n \"Call again with response_format='json' for every field Garmin returns.\",\n ].join(\"\\n\"),\n );\n },\n );\n\n server.registerTool(\n \"garmin_get_activity_splits\",\n {\n title: \"Splits & pacing analysis\",\n description: `Downloads the original .FIT recording and computes exact distance splits (default 1 km, interpolated at each boundary, pauses excluded), per-split HR, cadence and climb, the device laps, and a pacing verdict (even, negative split, faded).\n\nUse this for \"how was my pacing\", \"did I go out too fast\", \"compare km 1 to km 5\". Works for any GPS or footpod activity recorded on a Garmin device.`,\n inputSchema: z.strictObject({\n activity_id: ActivityId,\n split_meters: z.number().int().min(100).max(10_000).default(1000).describe(\"Split length in metres: 1000 for km, 1609 for miles, 400 for track laps\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, split_meters, response_format }, ctx) => {\n const bytes = await client.downloadActivity(activity_id, ctx.mcpReq.signal);\n const summary = summarizeFit(extractFit(bytes), split_meters);\n if (response_format === \"json\") return jsonResult(summary as unknown as Json);\n const s = summary.session;\n const unit = split_meters === 1000 ? \"km\" : `${split_meters} m`;\n const lines = [`# Activity ${activity_id}${s?.sport ? ` (${s.sport})` : \"\"}`];\n if (s) {\n lines.push(\n [\n s.distance_m !== undefined ? `${(s.distance_m / 1000).toFixed(2)} km` : undefined,\n `moving ${fmtDuration(s.timer_s)}`,\n s.distance_m && s.timer_s ? `avg ${fmtPace((s.timer_s / s.distance_m) * 1000)}` : undefined,\n s.avg_hr ? `HR ${s.avg_hr} avg / ${s.max_hr} max` : undefined,\n s.avg_cadence ? `cadence ${Math.round(s.avg_cadence)}` : undefined,\n s.total_ascent_m ? `+${s.total_ascent_m} m` : undefined,\n ]\n .filter(Boolean)\n .join(\" · \"),\n );\n }\n if (summary.paused_s > 5) lines.push(`Paused ${fmtDuration(summary.paused_s)} (excluded from split times).`);\n lines.push(\"\", `| ${unit} | dist | time | pace | avg HR | max HR | cadence | climb |`, \"|---|---|---|---|---|---|---|---|\");\n for (const sp of summary.splits) {\n lines.push(\n `| ${sp.index}${sp.partial ? \"*\" : \"\"} | ${(sp.distance_m / 1000).toFixed(2)} | ${fmtDuration(sp.moving_s)} | ${fmtPace(sp.pace_s_per_km)} | ${sp.avg_hr ?? \"–\"} | ${sp.max_hr ?? \"–\"} | ${sp.avg_cadence ?? \"–\"} | ${sp.elev_gain_m !== undefined ? `+${sp.elev_gain_m}` : \"–\"} |`,\n );\n }\n if (summary.splits.some((x) => x.partial)) lines.push(\"\", \"\\\\* partial split\");\n const p = summary.pacing;\n if (p) {\n lines.push(\n \"\",\n \"## Pacing\",\n `- ${p.verdict}`,\n `- First ${fmtPace(p.first_split_pace_s_per_km)}, last ${fmtPace(p.last_split_pace_s_per_km)}; fastest split ${p.fastest_split}, slowest ${p.slowest_split}`,\n `- Second half vs first half: ${p.half_split_delta_s_per_km > 0 ? \"+\" : \"\"}${p.half_split_delta_s_per_km} s/km`,\n `- Split-to-split variability: ${p.pace_variability_pct}% (under 3% is very even)`,\n );\n }\n if (summary.laps.length > 1) {\n lines.push(\"\", \"## Device laps\", \"| lap | dist | time | pace | avg HR |\", \"|---|---|---|---|---|\");\n for (const l of summary.laps) {\n lines.push(`| ${l.index} | ${(l.distance_m / 1000).toFixed(2)} | ${fmtDuration(l.timer_s)} | ${fmtPace(l.pace_s_per_km)} | ${l.avg_hr ?? \"–\"} |`);\n }\n }\n if (!summary.splits.length) lines.push(\"\", \"No distance data in this recording (e.g. a gym session), so there are no splits.\");\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_get_performance_metrics\",\n {\n title: \"VO2 max, training effect & recovery\",\n description: `Garmin's own VO2 max, read the way Garmin Connect shows it, plus per-activity aerobic/anaerobic training effect, recovery time and performance condition. Decoded from the original .FIT files, since the Personal API has no VO2 max endpoint.\n\nReturns the current VO2 max (whole number, as Garmin's apps round it, plus the precise value), every new VO2 max reading in the window (Garmin only updates it on qualifying runs and power-meter rides), and a row per activity.\n\nExamples:\n - \"what's my VO2 max?\" / \"how is my VO2 max trending?\" -> last_n=20\n - \"how hard was today's workout, how long to recover?\" -> last_n=1\nEach activity costs one file download, so keep last_n small unless a trend is asked for.`,\n inputSchema: z.strictObject({\n activity_id: ActivityId.optional().describe(\"One activity, from garmin_list_activities. Omit to scan recent activities\"),\n last_n: z.number().int().min(1).max(30).default(10).describe(\"How many recent activities to scan (all types)\"),\n activity_type: z.string().optional().describe(\"Only this Garmin type key, e.g. running, cycling\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, last_n, activity_type, response_format }, ctx) => {\n const signal = ctx.mcpReq.signal;\n const targets: { id: string; name?: string; type?: string; start?: string }[] = [];\n if (activity_id) {\n const a = summarizeActivity(await client.activity(activity_id, signal));\n targets.push({ id: activity_id, name: a?.name, type: a?.type, start: a?.start_local });\n } else {\n const raw = await client.searchActivities({ limit: last_n, activityType: activity_type }, signal);\n const list = Array.isArray(raw) ? raw : isObj(raw) && Array.isArray(raw.activityList) ? raw.activityList : [];\n for (const a of list.map(summarizeActivity)) if (a) targets.push({ id: String(a.activity_id), name: a.name, type: a.type, start: a.start_local });\n }\n const rows = await mapLimit(targets, 3, async (t) => {\n try {\n return { ...t, m: performanceMetrics(decodeFit(extractFit(await client.downloadActivity(t.id, signal)))) };\n } catch (e) {\n return { ...t, skipped: (e as Error).message };\n }\n });\n const ok = rows.filter((r): r is typeof r & { m: PerformanceMetrics } => \"m\" in r && !!r.m);\n const readings = ok.filter((r) => r.m.new_vo2max_reading && r.m.vo2max_after);\n const newest = ok[0];\n const current = newest?.m.new_vo2max_reading ? newest.m.vo2max_after : (newest?.m.vo2max_before ?? readings[0]?.m.vo2max_after);\n const result = {\n vo2max: current === undefined ? undefined : { value: Math.round(current), precise: current, as_of: newest?.start?.slice(0, 10), last_reading: readings[0]?.start?.slice(0, 10) },\n vo2max_readings: readings.map((r) => ({\n date: r.start?.slice(0, 10),\n activity_id: r.id,\n type: r.type,\n vo2max: Math.round(r.m.vo2max_after!),\n precise: r.m.vo2max_after,\n change: r.m.vo2max_before !== undefined ? Math.round((r.m.vo2max_after! - r.m.vo2max_before) * 100) / 100 : undefined,\n })),\n activities: ok.map((r) => ({ date: r.start, activity_id: r.id, name: r.name, type: r.type, ...r.m })),\n skipped: rows.filter((r) => \"skipped\" in r).map((r) => ({ activity_id: r.id, type: r.type, reason: (r as { skipped: string }).skipped })),\n note: \"VO2 max and training effect match Garmin Connect. Recovery time and performance condition come from undocumented FIT fields; treat them as approximate.\",\n };\n if (response_format === \"json\") return jsonResult(result as unknown as Json);\n\n const lines: string[] = [];\n if (result.vo2max) {\n const v = result.vo2max;\n lines.push(`# VO2 max: ${v.value} ml/kg/min`, `Precise ${v.precise.toFixed(2)} as of ${v.as_of}${v.last_reading ? `; last updated by the ${v.last_reading} activity` : \"; no new reading in this window\"}.`);\n } else {\n lines.push(\"# VO2 max: not available\", \"None of these activities carry a VO2 max. Garmin needs a qualifying outdoor run (or a ride with a power meter) to estimate one.\");\n }\n if (result.vo2max_readings.length) {\n lines.push(\"\", \"## VO2 max readings\", \"| date | activity | VO2 max | change |\", \"|---|---|---|---|\");\n for (const r of result.vo2max_readings) lines.push(`| ${r.date} | ${r.type ?? \"?\"} | ${r.vo2max} (${r.precise?.toFixed(2)}) | ${r.change === undefined ? \"–\" : `${r.change > 0 ? \"+\" : \"\"}${r.change.toFixed(2)}`} |`);\n }\n if (ok.length) {\n lines.push(\"\", \"## Per activity\", \"| date | activity | aerobic TE | anaerobic TE | recovery | perf. condition |\", \"|---|---|---|---|---|---|\");\n for (const r of ok) {\n const m = r.m;\n lines.push(`| ${r.start?.slice(0, 16) ?? \"?\"} | ${r.name ?? r.type ?? r.id} | ${m.training_effect_aerobic ?? \"–\"} | ${m.training_effect_anaerobic ?? \"–\"} | ${m.recovery_time_h !== undefined ? `${m.recovery_time_h} h` : \"–\"} | ${m.performance_condition !== undefined ? `${m.performance_condition > 0 ? \"+\" : \"\"}${m.performance_condition}` : \"–\"} |`);\n }\n }\n if (result.skipped.length) lines.push(\"\", `Skipped ${result.skipped.length} without a FIT file (e.g. manual entries): ${result.skipped.map((s) => s.type ?? s.activity_id).join(\", \")}.`);\n lines.push(\"\", `_${result.note}_`);\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_download_activity_fit\",\n {\n title: \"Save original activity file\",\n description:\n \"Saves an activity's original file (Garmin's ZIP containing the .fit) to a folder on this computer, for import into other tools such as Runalyze or Golden Cheetah. Returns the saved path.\",\n inputSchema: z.strictObject({\n activity_id: ActivityId,\n directory: z.string().optional().describe(\"Folder to save into. Defaults to ~/Downloads/garmin\"),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },\n },\n async ({ activity_id, directory }, ctx) => {\n const bytes = await client.downloadActivity(activity_id, ctx.mcpReq.signal);\n const dir = resolve((directory ?? join(homedir(), \"Downloads\", \"garmin\")).replace(/^~(?=$|\\/)/, homedir()));\n mkdirSync(dir, { recursive: true });\n const isZip = bytes[0] === 0x50 && bytes[1] === 0x4b;\n const file = join(dir, `${activity_id}.${isZip ? \"zip\" : \"fit\"}`);\n writeFileSync(file, bytes);\n return textResult(`Saved ${(bytes.length / 1024).toFixed(0)} KB to ${file}`);\n },\n );\n\n // ---------------- Day overview ----------------\n server.registerTool(\n \"garmin_get_day\",\n {\n title: \"One day at a glance\",\n description:\n \"Everything for one day in one call: daily summary (steps, calories, resting HR, stress, intensity minutes), sleep, overnight HRV, Body Battery and activities. Best first call for 'how recovered am I today' or 'how was yesterday'. Parts Garmin has no data for are omitted.\",\n inputSchema: z.strictObject({ date: DateArg.default(\"today\") }),\n annotations: READ_ONLY,\n },\n async ({ date }, ctx) => {\n const d = resolveDate(date);\n const s = ctx.mcpReq.signal;\n const settle = async (p: Promise<Json>) => p.catch((e: Error) => ({ unavailable: e.message }) as Json);\n const [summary, sleep, hrv, bodyBattery, activities] = await Promise.all([\n settle(client.range(\"dailySummaries\", d, d, s)),\n settle(client.dailySleep(d, s)),\n settle(client.dailyHrv(d, s)),\n settle(client.range(\"bodyBattery\", d, d, s)),\n settle(client.searchActivities({ startDate: d, endDate: d, limit: 20 }, s)),\n ]);\n const acts = (Array.isArray(activities) ? activities : []).map(summarizeActivity).filter(Boolean) as unknown as Json[];\n return jsonResult({ date: d, daily_summary: summary, sleep, hrv, body_battery: bodyBattery, activities: acts.length ? acts : activities });\n },\n );\n\n // ---------------- Ranges ----------------\n const rangeTool = (name: string, endpoint: RangeEndpoint, title: string, description: string) =>\n server.registerTool(\n name,\n {\n title,\n description: `${description}\\n\\nLong ranges are fetched in chunks automatically (up to 366 days).`,\n inputSchema: z.strictObject({ start_date: StartArg, end_date: EndArg }),\n annotations: READ_ONLY,\n },\n async ({ start_date, end_date }, ctx) => {\n const [a, b] = resolveRange(start_date, end_date);\n return jsonResult(await fetchRange(client, endpoint, a, b, ctx.mcpReq.signal), `${title}, ${a} to ${b}.`);\n },\n );\n\n rangeTool(\n \"garmin_get_daily_summaries\",\n \"dailySummaries\",\n \"Daily summaries\",\n \"Per-day totals: steps, distance, active and total calories, resting/min/max heart rate, average and max stress, intensity minutes, floors, Body Battery high/low. Best tool for trends across weeks.\",\n );\n rangeTool(\"garmin_get_sleep\", \"sleep\", \"Sleep\", \"Per-night sleep: duration, deep/light/REM/awake time, sleep score, and timing.\");\n rangeTool(\"garmin_get_hrv\", \"hrv\", \"HRV\", \"Per-night heart-rate variability: last-night average, weekly average, baseline range and status (balanced, unbalanced, low).\");\n rangeTool(\"garmin_get_body_battery\", \"bodyBattery\", \"Body Battery\", \"Per-day Body Battery: charged, drained, highest and lowest values.\");\n rangeTool(\"garmin_get_stress\", \"stress\", \"Stress\", \"Per-day all-day stress levels.\");\n rangeTool(\"garmin_get_steps\", \"steps\", \"Steps\", \"Per-day step totals and goals.\");\n rangeTool(\"garmin_get_hydration\", \"hydration\", \"Hydration\", \"Per-day logged water intake and goal.\");\n rangeTool(\n \"garmin_get_weight\",\n \"weight\",\n \"Weight & body composition\",\n \"Every weigh-in in the range (Garmin usually reports weight in grams: divide by 1000 for kg), with BMI, body fat, muscle mass and water where a smart scale recorded them. Use for bulk/cut progress.\",\n );\n rangeTool(\"garmin_get_weight_goals\", \"weightGoals\", \"Weight goals\", \"Weight goals in effect during the range.\");\n rangeTool(\n \"garmin_get_wellness_activities\",\n \"wellnessActivities\",\n \"Wellness activities\",\n \"Wellness activity summaries (e.g. breathwork and relaxation sessions) for the range.\",\n );\n\n // ---------------- Single-day detail ----------------\n const dayTool = (name: string, title: string, description: string, fn: (d: string, s: AbortSignal) => Promise<Json>) =>\n server.registerTool(\n name,\n { title, description, inputSchema: z.strictObject({ date: DateArg.default(\"today\") }), annotations: READ_ONLY },\n async ({ date }, ctx) => {\n const d = resolveDate(date);\n return jsonResult(await fn(d, ctx.mcpReq.signal), `${title}, ${d}.`);\n },\n );\n\n dayTool(\"garmin_get_sleep_detail\", \"Sleep detail\", \"One night's sleep in full: stages timeline, sleep score breakdown, respiration, restless moments, SpO2 and HR during sleep.\", client.dailySleep);\n dayTool(\"garmin_get_heart_rate\", \"Heart rate (all day)\", \"One day's resting HR and the all-day heart-rate samples.\", client.dailyHeartRate);\n dayTool(\"garmin_get_spo2\", \"Pulse Ox\", \"One day's SpO2 readings and acclimation.\", client.dailySpo2);\n dayTool(\"garmin_get_fitness_age\", \"Fitness age\", \"Garmin's fitness age estimate and its inputs for a date.\", client.fitnessAge);\n dayTool(\"garmin_get_nutrition\", \"Food log\", \"Foods and macros logged in Garmin Connect's nutrition log for one day.\", client.foodLogs);\n\n // ---------------- Escape hatch ----------------\n server.registerTool(\n \"garmin_api_get\",\n {\n title: \"Raw Personal API GET\",\n description: `Call any documented Personal API endpoint directly, for data the other tools don't cover (e.g. menstrual cycle summaries, activity workouts).\nPath must start with one of: ${ALLOWED_PATH_PREFIXES.join(\", \")}.\nExample: path=\"/activity-service/activity/123456/workouts\"`,\n inputSchema: z.strictObject({\n path: z.string().startsWith(\"/\").describe(\"API path beginning with /\"),\n params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional().describe(\"Query parameters\"),\n }),\n annotations: READ_ONLY,\n },\n async ({ path, params }, ctx) => {\n if (path.includes(\"..\") || !ALLOWED_PATH_PREFIXES.some((p) => path.startsWith(p))) {\n throw new Error(`Path not allowed. It must start with one of ${ALLOWED_PATH_PREFIXES.join(\", \")}.`);\n }\n return jsonResult(await client.get(path, params, ctx.mcpReq.signal));\n },\n );\n}\n\n/** Runs `fn` over `items` with at most `limit` in flight, keeping input order. */\nasync function mapLimit<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const out: R[] = new Array(items.length);\n let next = 0;\n const worker = async () => {\n while (next < items.length) {\n const i = next++;\n out[i] = await fn(items[i]);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdirSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport * as z from \"zod/v4\";\nimport {\n ALLOWED_PATH_PREFIXES,\n fetchRange,\n type GarminClient,\n type Json,\n type RangeEndpoint,\n resolveDate,\n resolveRange,\n} from \"./api.js\";\nimport { decodeFit, extractFit, fmtDuration, fmtPace, type PerformanceMetrics, performanceMetrics, summarizeFit } from \"./fit.js\";\nimport { activityLine, isObj, jsonResult, summarizeActivity, textResult } from \"./format.js\";\n\nconst READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true } as const;\n\nconst DateArg = z\n .string()\n .min(1)\n .describe('Date as YYYY-MM-DD, or \"today\", \"yesterday\", \"N days ago\" (local time)');\nconst StartArg = DateArg.describe('First day, inclusive: YYYY-MM-DD, \"today\", \"yesterday\" or \"N days ago\"');\nconst EndArg = z.string().optional().describe('Last day, inclusive. Defaults to today. Same formats as start_date');\nconst ActivityId = z\n .union([z.string().regex(/^\\d+$/), z.number().int().positive()])\n .transform(String)\n .describe(\"Numeric Garmin activity ID, from garmin_list_activities\");\nconst Format = z.enum([\"markdown\", \"json\"]).default(\"markdown\").describe(\"'markdown' for a readable summary, 'json' for every field\");\n\nexport function registerTools(server: McpServer, client: GarminClient): void {\n // ---------------- Profile ----------------\n server.registerTool(\n \"garmin_get_profile\",\n {\n title: \"Profile & heart-rate zones\",\n description:\n \"The user's Garmin profile settings (units, weight, height, birth year, lactate threshold where set) plus their heart-rate zones. Call this first when you need zones or units to interpret other data.\",\n inputSchema: z.strictObject({}),\n annotations: READ_ONLY,\n },\n async (_args, ctx) => {\n const s = ctx.mcpReq.signal;\n const [profile, zones] = await Promise.all([client.profileSettings(s), client.heartRateZones(s).catch((e: Error) => ({ error: e.message }))]);\n return jsonResult({ profile, heart_rate_zones: zones as Json });\n },\n );\n\n // ---------------- Activities ----------------\n server.registerTool(\n \"garmin_list_activities\",\n {\n title: \"List activities\",\n description: `List recorded activities (runs, gym sessions, rides…), newest first, with distance, time, pace, heart rate, training effect and Garmin's training load.\n\nExamples:\n - \"my runs this month\" -> start_date=\"2026-10-01\", activity_type=\"running\"\n - \"last 5 activities\" -> limit=5\nUse garmin_get_activity_splits on an activity_id for per-km splits and pacing.`,\n inputSchema: z.strictObject({\n start_date: z.string().optional().describe(\"Only activities on/after this day (YYYY-MM-DD, 'today', 'N days ago')\"),\n end_date: z.string().optional().describe(\"Only activities on/before this day\"),\n activity_type: z\n .string()\n .optional()\n .describe(\"Garmin type key, e.g. running, treadmill_running, trail_running, cycling, indoor_cycling, strength_training, walking\"),\n limit: z.number().int().min(1).max(100).default(20).describe(\"Max activities to return\"),\n offset: z.number().int().min(0).default(0).describe(\"Skip this many, for paging\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ start_date, end_date, activity_type, limit, offset, response_format }, ctx) => {\n const raw = await client.searchActivities(\n {\n start: offset,\n limit,\n startDate: start_date ? resolveDate(start_date) : undefined,\n endDate: end_date ? resolveDate(end_date) : undefined,\n activityType: activity_type,\n },\n ctx.mcpReq.signal,\n );\n const list = Array.isArray(raw) ? raw : isObj(raw) && Array.isArray(raw.activityList) ? raw.activityList : [];\n if (response_format === \"json\") return jsonResult({ offset, count: list.length, has_more: list.length === limit, activities: list });\n const items = list.map(summarizeActivity).filter((a) => a !== undefined);\n if (!items.length) return textResult(\"No activities match. Widen the date range or drop activity_type.\");\n const lines = [`# ${items.length} activities`, \"\", ...items.map(activityLine)];\n if (list.length === limit) lines.push(\"\", `More may exist: call again with offset=${offset + limit}.`);\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_get_activity\",\n {\n title: \"Activity details\",\n description:\n \"Garmin's computed summary for one activity: distance, durations, pace/speed, HR, cadence, elevation, training effect, calories. For per-km splits use garmin_get_activity_splits.\",\n inputSchema: z.strictObject({ activity_id: ActivityId, response_format: Format }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, response_format }, ctx) => {\n const raw = await client.activity(activity_id, ctx.mcpReq.signal);\n if (response_format === \"json\") return jsonResult(raw);\n const a = summarizeActivity(raw);\n if (!a) return jsonResult(raw, \"Could not summarise this activity; raw data follows.\");\n return textResult(\n [\n `# ${a.name ?? \"Activity\"} (${a.type ?? \"?\"})`,\n ...Object.entries(a)\n .filter(([k, v]) => v !== undefined && k !== \"name\" && k !== \"type\")\n .map(([k, v]) => `- ${k.replace(/_/g, \" \")}: ${v}`),\n \"\",\n \"Call again with response_format='json' for every field Garmin returns.\",\n ].join(\"\\n\"),\n );\n },\n );\n\n server.registerTool(\n \"garmin_get_activity_splits\",\n {\n title: \"Splits & pacing analysis\",\n description: `Downloads the original .FIT recording and computes exact distance splits (default 1 km, interpolated at each boundary, pauses excluded), per-split HR, cadence and climb, the device laps, and a pacing verdict (even, negative split, faded).\n\nUse this for \"how was my pacing\", \"did I go out too fast\", \"compare km 1 to km 5\". Works for any GPS or footpod activity recorded on a Garmin device.`,\n inputSchema: z.strictObject({\n activity_id: ActivityId,\n split_meters: z.number().int().min(100).max(10_000).default(1000).describe(\"Split length in metres: 1000 for km, 1609 for miles, 400 for track laps\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, split_meters, response_format }, ctx) => {\n const bytes = await client.downloadActivity(activity_id, ctx.mcpReq.signal);\n const summary = summarizeFit(extractFit(bytes), split_meters);\n if (response_format === \"json\") return jsonResult(summary as unknown as Json);\n const s = summary.session;\n const unit = split_meters === 1000 ? \"km\" : `${split_meters} m`;\n const lines = [`# Activity ${activity_id}${s?.sport ? ` (${s.sport})` : \"\"}`];\n if (s) {\n lines.push(\n [\n s.distance_m !== undefined ? `${(s.distance_m / 1000).toFixed(2)} km` : undefined,\n `moving ${fmtDuration(s.timer_s)}`,\n s.distance_m && s.timer_s ? `avg ${fmtPace((s.timer_s / s.distance_m) * 1000)}` : undefined,\n s.avg_hr ? `HR ${s.avg_hr} avg / ${s.max_hr} max` : undefined,\n s.avg_cadence ? `cadence ${Math.round(s.avg_cadence)}` : undefined,\n s.total_ascent_m ? `+${s.total_ascent_m} m` : undefined,\n ]\n .filter(Boolean)\n .join(\" · \"),\n );\n }\n if (summary.paused_s > 5) lines.push(`Paused ${fmtDuration(summary.paused_s)} (excluded from split times).`);\n lines.push(\"\", `| ${unit} | dist | time | pace | avg HR | max HR | cadence | climb |`, \"|---|---|---|---|---|---|---|---|\");\n for (const sp of summary.splits) {\n lines.push(\n `| ${sp.index}${sp.partial ? \"*\" : \"\"} | ${(sp.distance_m / 1000).toFixed(2)} | ${fmtDuration(sp.moving_s)} | ${fmtPace(sp.pace_s_per_km)} | ${sp.avg_hr ?? \"–\"} | ${sp.max_hr ?? \"–\"} | ${sp.avg_cadence ?? \"–\"} | ${sp.elev_gain_m !== undefined ? `+${sp.elev_gain_m}` : \"–\"} |`,\n );\n }\n if (summary.splits.some((x) => x.partial)) lines.push(\"\", \"\\\\* partial split\");\n const p = summary.pacing;\n if (p) {\n lines.push(\n \"\",\n \"## Pacing\",\n `- ${p.verdict}`,\n `- First ${fmtPace(p.first_split_pace_s_per_km)}, last ${fmtPace(p.last_split_pace_s_per_km)}; fastest split ${p.fastest_split}, slowest ${p.slowest_split}`,\n `- Second half vs first half: ${p.half_split_delta_s_per_km > 0 ? \"+\" : \"\"}${p.half_split_delta_s_per_km} s/km`,\n `- Split-to-split variability: ${p.pace_variability_pct}% (under 3% is very even)`,\n );\n }\n if (summary.laps.length > 1) {\n lines.push(\"\", \"## Device laps\", \"| lap | dist | time | pace | avg HR |\", \"|---|---|---|---|---|\");\n for (const l of summary.laps) {\n lines.push(`| ${l.index} | ${(l.distance_m / 1000).toFixed(2)} | ${fmtDuration(l.timer_s)} | ${fmtPace(l.pace_s_per_km)} | ${l.avg_hr ?? \"–\"} |`);\n }\n }\n if (!summary.splits.length) lines.push(\"\", \"No distance data in this recording (e.g. a gym session), so there are no splits.\");\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_get_performance_metrics\",\n {\n title: \"VO2 max, training effect & recovery\",\n description: `Garmin's own VO2 max, read the way Garmin Connect shows it, plus per-activity aerobic/anaerobic training effect, recovery time and performance condition. Decoded from the original .FIT files, since the Personal API has no VO2 max endpoint.\n\nReturns the current VO2 max (whole number, as Garmin's apps round it, plus the precise value), every new VO2 max reading in the window (Garmin only updates it on qualifying runs and power-meter rides), and a row per activity.\n\nExamples:\n - \"what's my VO2 max?\" / \"how is my VO2 max trending?\" -> last_n=20\n - \"how hard was today's workout, how long to recover?\" -> last_n=1\nEach activity costs one file download, so keep last_n small unless a trend is asked for.`,\n inputSchema: z.strictObject({\n activity_id: ActivityId.optional().describe(\"One activity, from garmin_list_activities. Omit to scan recent activities\"),\n last_n: z.number().int().min(1).max(30).default(10).describe(\"How many recent activities to scan (all types)\"),\n activity_type: z.string().optional().describe(\"Only this Garmin type key, e.g. running, cycling\"),\n response_format: Format,\n }),\n annotations: READ_ONLY,\n },\n async ({ activity_id, last_n, activity_type, response_format }, ctx) => {\n const signal = ctx.mcpReq.signal;\n const targets: { id: string; name?: string; type?: string; start?: string }[] = [];\n if (activity_id) {\n const a = summarizeActivity(await client.activity(activity_id, signal));\n targets.push({ id: activity_id, name: a?.name, type: a?.type, start: a?.start_local });\n } else {\n const raw = await client.searchActivities({ limit: last_n, activityType: activity_type }, signal);\n const list = Array.isArray(raw) ? raw : isObj(raw) && Array.isArray(raw.activityList) ? raw.activityList : [];\n for (const a of list.map(summarizeActivity)) if (a) targets.push({ id: String(a.activity_id), name: a.name, type: a.type, start: a.start_local });\n }\n const rows = await mapLimit(targets, 3, async (t) => {\n try {\n return { ...t, m: performanceMetrics(decodeFit(extractFit(await client.downloadActivity(t.id, signal)))) };\n } catch (e) {\n return { ...t, skipped: (e as Error).message };\n }\n });\n const ok = rows.filter((r): r is typeof r & { m: PerformanceMetrics } => \"m\" in r && !!r.m);\n const readings = ok.filter((r) => r.m.new_vo2max_reading && r.m.vo2max_after);\n const newest = ok[0];\n const current = newest?.m.new_vo2max_reading ? newest.m.vo2max_after : (newest?.m.vo2max_before ?? readings[0]?.m.vo2max_after);\n const result = {\n vo2max: current === undefined ? undefined : { value: Math.round(current), precise: current, as_of: newest?.start?.slice(0, 10), last_reading: readings[0]?.start?.slice(0, 10) },\n vo2max_readings: readings.map((r) => ({\n date: r.start?.slice(0, 10),\n activity_id: r.id,\n type: r.type,\n vo2max: Math.round(r.m.vo2max_after!),\n precise: r.m.vo2max_after,\n change: r.m.vo2max_before !== undefined ? Math.round((r.m.vo2max_after! - r.m.vo2max_before) * 100) / 100 : undefined,\n })),\n activities: ok.map((r) => ({ date: r.start, activity_id: r.id, name: r.name, type: r.type, ...r.m })),\n skipped: rows.filter((r) => \"skipped\" in r).map((r) => ({ activity_id: r.id, type: r.type, reason: (r as { skipped: string }).skipped })),\n note: \"VO2 max and training effect match Garmin Connect. Recovery time and performance condition come from undocumented FIT fields; treat them as approximate.\",\n };\n if (response_format === \"json\") return jsonResult(result as unknown as Json);\n\n const lines: string[] = [];\n if (result.vo2max) {\n const v = result.vo2max;\n lines.push(`# VO2 max: ${v.value} ml/kg/min`, `Precise ${v.precise.toFixed(2)} as of ${v.as_of}${v.last_reading ? `; last updated by the ${v.last_reading} activity` : \"; no new reading in this window\"}.`);\n } else {\n lines.push(\"# VO2 max: not available\", \"None of these activities carry a VO2 max. Garmin needs a qualifying outdoor run (or a ride with a power meter) to estimate one.\");\n }\n if (result.vo2max_readings.length) {\n lines.push(\"\", \"## VO2 max readings\", \"| date | activity | VO2 max | change |\", \"|---|---|---|---|\");\n for (const r of result.vo2max_readings) lines.push(`| ${r.date} | ${r.type ?? \"?\"} | ${r.vo2max} (${r.precise?.toFixed(2)}) | ${r.change === undefined ? \"–\" : `${r.change > 0 ? \"+\" : \"\"}${r.change.toFixed(2)}`} |`);\n }\n if (ok.length) {\n lines.push(\"\", \"## Per activity\", \"| date | activity | aerobic TE | anaerobic TE | recovery | perf. condition |\", \"|---|---|---|---|---|---|\");\n for (const r of ok) {\n const m = r.m;\n lines.push(`| ${r.start?.slice(0, 16) ?? \"?\"} | ${r.name ?? r.type ?? r.id} | ${m.training_effect_aerobic ?? \"–\"} | ${m.training_effect_anaerobic ?? \"–\"} | ${m.recovery_time_h !== undefined ? `${m.recovery_time_h} h` : \"–\"} | ${m.performance_condition !== undefined ? `${m.performance_condition > 0 ? \"+\" : \"\"}${m.performance_condition}` : \"–\"} |`);\n }\n }\n if (result.skipped.length) lines.push(\"\", `Skipped ${result.skipped.length} without a FIT file (e.g. manual entries): ${result.skipped.map((s) => s.type ?? s.activity_id).join(\", \")}.`);\n lines.push(\"\", `_${result.note}_`);\n return textResult(lines.join(\"\\n\"));\n },\n );\n\n server.registerTool(\n \"garmin_download_activity_fit\",\n {\n title: \"Save original activity file\",\n description:\n \"Saves an activity's original file (Garmin's ZIP containing the .fit) to a folder on this computer, for import into other tools such as Runalyze or Golden Cheetah. Returns the saved path.\",\n inputSchema: z.strictObject({\n activity_id: ActivityId,\n directory: z.string().optional().describe(\"Folder to save into. Defaults to ~/Downloads/garmin\"),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },\n },\n async ({ activity_id, directory }, ctx) => {\n const bytes = await client.downloadActivity(activity_id, ctx.mcpReq.signal);\n const dir = resolve((directory ?? join(homedir(), \"Downloads\", \"garmin\")).replace(/^~(?=$|\\/)/, homedir()));\n mkdirSync(dir, { recursive: true });\n const isZip = bytes[0] === 0x50 && bytes[1] === 0x4b;\n const file = join(dir, `${activity_id}.${isZip ? \"zip\" : \"fit\"}`);\n writeFileSync(file, bytes);\n return textResult(`Saved ${(bytes.length / 1024).toFixed(0)} KB to ${file}`);\n },\n );\n\n // ---------------- Day overview ----------------\n server.registerTool(\n \"garmin_get_day\",\n {\n title: \"One day at a glance\",\n description:\n \"Everything for one day in one call: daily summary (steps, calories, resting HR, stress, intensity minutes), sleep, overnight HRV, Body Battery and activities. Best first call for 'how recovered am I today' or 'how was yesterday'. Parts Garmin has no data for are omitted.\",\n inputSchema: z.strictObject({ date: DateArg.default(\"today\") }),\n annotations: READ_ONLY,\n },\n async ({ date }, ctx) => {\n const d = resolveDate(date);\n const s = ctx.mcpReq.signal;\n const settle = async (p: Promise<Json>) => p.catch((e: Error) => ({ unavailable: e.message }) as Json);\n const [summary, sleep, hrv, bodyBattery, activities] = await Promise.all([\n settle(client.range(\"dailySummaries\", d, d, s)),\n settle(client.dailySleep(d, s)),\n settle(client.dailyHrv(d, s)),\n settle(client.range(\"bodyBattery\", d, d, s)),\n settle(client.searchActivities({ startDate: d, endDate: d, limit: 20 }, s)),\n ]);\n const acts = (Array.isArray(activities) ? activities : []).map(summarizeActivity).filter(Boolean) as unknown as Json[];\n return jsonResult({ date: d, daily_summary: summary, sleep, hrv, body_battery: bodyBattery, activities: acts.length ? acts : activities });\n },\n );\n\n // ---------------- Ranges ----------------\n const rangeTool = (name: string, endpoint: RangeEndpoint, title: string, description: string) =>\n server.registerTool(\n name,\n {\n title,\n description: `${description}\\n\\nLong ranges are fetched in chunks automatically (up to 366 days).`,\n inputSchema: z.strictObject({ start_date: StartArg, end_date: EndArg }),\n annotations: READ_ONLY,\n },\n async ({ start_date, end_date }, ctx) => {\n const [a, b] = resolveRange(start_date, end_date);\n return jsonResult(await fetchRange(client, endpoint, a, b, ctx.mcpReq.signal), `${title}, ${a} to ${b}.`);\n },\n );\n\n rangeTool(\n \"garmin_get_daily_summaries\",\n \"dailySummaries\",\n \"Daily summaries\",\n \"Per-day totals: steps, distance, active and total calories, resting/min/max heart rate, average and max stress, intensity minutes, floors, Body Battery high/low. Best tool for trends across weeks.\",\n );\n rangeTool(\"garmin_get_sleep\", \"sleep\", \"Sleep\", \"Per-night sleep: duration, deep/light/REM/awake time, sleep score, and timing.\");\n rangeTool(\"garmin_get_hrv\", \"hrv\", \"HRV\", \"Per-night heart-rate variability: last-night average, weekly average, baseline range and status (balanced, unbalanced, low).\");\n rangeTool(\"garmin_get_body_battery\", \"bodyBattery\", \"Body Battery\", \"Per-day Body Battery: charged, drained, highest and lowest values.\");\n rangeTool(\"garmin_get_stress\", \"stress\", \"Stress\", \"Per-day all-day stress levels.\");\n rangeTool(\"garmin_get_steps\", \"steps\", \"Steps\", \"Per-day step totals and goals.\");\n rangeTool(\"garmin_get_hydration\", \"hydration\", \"Hydration\", \"Per-day logged water intake and goal.\");\n rangeTool(\n \"garmin_get_weight\",\n \"weight\",\n \"Weight & body composition\",\n \"Every weigh-in in the range (Garmin usually reports weight in grams: divide by 1000 for kg), with BMI, body fat, muscle mass and water where a smart scale recorded them. Use for bulk/cut progress.\",\n );\n rangeTool(\"garmin_get_weight_goals\", \"weightGoals\", \"Weight goals\", \"Weight goals in effect during the range.\");\n rangeTool(\n \"garmin_get_wellness_activities\",\n \"wellnessActivities\",\n \"Wellness activities\",\n \"Wellness activity summaries (e.g. breathwork and relaxation sessions) for the range.\",\n );\n\n // ---------------- Single-day detail ----------------\n const dayTool = (name: string, title: string, description: string, fn: (d: string, s: AbortSignal) => Promise<Json>) =>\n server.registerTool(\n name,\n { title, description, inputSchema: z.strictObject({ date: DateArg.default(\"today\") }), annotations: READ_ONLY },\n async ({ date }, ctx) => {\n const d = resolveDate(date);\n return jsonResult(await fn(d, ctx.mcpReq.signal), `${title}, ${d}.`);\n },\n );\n\n dayTool(\"garmin_get_sleep_detail\", \"Sleep detail\", \"One night's sleep in full: stages timeline, sleep score breakdown, respiration, restless moments, SpO2 and HR during sleep.\", client.dailySleep);\n dayTool(\"garmin_get_heart_rate\", \"Heart rate (all day)\", \"One day's resting HR and the all-day heart-rate samples.\", client.dailyHeartRate);\n dayTool(\"garmin_get_spo2\", \"Pulse Ox\", \"One day's SpO2 readings and acclimation.\", client.dailySpo2);\n dayTool(\"garmin_get_fitness_age\", \"Fitness age\", \"Garmin's fitness age estimate and its inputs for a date.\", client.fitnessAge);\n dayTool(\"garmin_get_nutrition\", \"Food log\", \"Foods and macros logged in Garmin Connect's nutrition log for one day.\", client.foodLogs);\n\n // ---------------- Escape hatch ----------------\n server.registerTool(\n \"garmin_api_get\",\n {\n title: \"Raw Personal API GET\",\n description: `Call any documented Personal API endpoint directly, for data the other tools don't cover (e.g. menstrual cycle summaries, activity workouts).\nPath must start with one of: ${ALLOWED_PATH_PREFIXES.join(\", \")}.\nExample: path=\"/activity-service/activity/123456/workouts\"`,\n inputSchema: z.strictObject({\n path: z.string().startsWith(\"/\").describe(\"API path beginning with /\"),\n params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional().describe(\"Query parameters\"),\n }),\n annotations: READ_ONLY,\n },\n async ({ path, params }, ctx) => {\n if (path.includes(\"..\") || !ALLOWED_PATH_PREFIXES.some((p) => path.startsWith(p))) {\n throw new Error(`Path not allowed. It must start with one of ${ALLOWED_PATH_PREFIXES.join(\", \")}.`);\n }\n return jsonResult(await client.get(path, params, ctx.mcpReq.signal));\n },\n );\n}\n\n/** Runs `fn` over `items` with at most `limit` in flight, keeping input order. */\nasync function mapLimit<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const out: R[] = new Array(items.length);\n let next = 0;\n const worker = async () => {\n while (next < items.length) {\n const i = next++;\n out[i] = await fn(items[i]);\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));\n return out;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "garmin-personal-api-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCP server for your own Garmin Connect data (activities with FIT splits, sleep, HRV, Body Battery, weight) via Garmin's official Personal API, with an interactive key setup wizard.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"scripts": {
|