MCP App Store
by KeenEthics

Overview

Use your Lion Lift workout history to plan runs, pool swims, bike rides, and lifts; review progress across sports; compare planned and completed sessions; and create training reports.

Tools

advanced_analysis_query

ChatGPT
ADVANCED TOOL. Use the regular tools above (fetch_records, list_run_workouts, list_meals, etc.) for anything recent, anything about "today"/"this week," or any normal question — they're simpler and always current. Reach for this only for deep historical analysis across a lot of records at once, where you genuinely need to filter, join, or aggregate — e.g. every workout that included a specific exercise, a weight-progression trend over months, average volume by muscle group over a long window. Call advanced_analysis_schema first if you haven't already, to see the real table/column names. sql: a single read-only SELECT (or WITH ... SELECT) statement — this connection has no write access at the database level regardless, and only ever sees this one account's own rows, enforced independently of anything this query text says. No semicolons (one statement per call). Results are capped at 500 rows regardless of what you ask for. This data source refreshes roughly once a day, not live. See advanced_analysis_schema's docstring for exactly how to phrase that to the user in plain language, and what an error here usually means (not set up / not synced yet — don't retry, fall back to the regular tools instead).

advanced_analysis_schema

ChatGPT
ADVANCED TOOL — see advanced_analysis_query's docstring for when to reach for this pair instead of the regular tools above. Call this first, before advanced_analysis_query, to see exactly what's queryable: every table and column currently available. Lift history only for now (lift_workouts, lift_exercises, lift_sets) — nothing for run/bike/swim/core/meals yet. Worth saying plainly to the user the first time you use this pair of tools: this data source refreshes roughly once a day, not live — so something logged in the last few hours may not show up here yet. When you mention this, don't describe it in technical terms (don't say "SQL," "database," "sync job," "schema," or "nightly batch") — just something like "I have a deeper analysis tool that looks across your full history, but it updates about once a day, so anything from today might not be in it yet." If this (or advanced_analysis_query) ever errors, it almost always means this hasn't synced for this account yet, or isn't fully set up yet — not that anything is wrong with your question. Don't retry it; just fall back to the regular tools above for this request.

fetch_records

ChatGPT
General-purpose read: query collection with any combination of a date range and arbitrary field filters. Works for one record (limit=1) or many — no separate bulk tool needed, and no field/filter is hardcoded — any field that exists (see list_fields) can be filtered, ordered, or projected on. start_date / end_date: ISO 8601 date or datetime strings (e.g. "2026-09-01"), applied to whichever field this collection actually uses for its date (resolved automatically per collection). A bare date with no time component is inclusive on both ends — start_date="2026-09-17", end_date="2026-09-17" means the whole day, not a zero-width instant at midnight. Records' dates are stored in raw UTC (not local time), so a workout logged late evening in a US timezone can appear one calendar day later than the user experienced it — when in doubt near a day boundary, widen the range by a day on each side rather than trust an exact single-day match. filters: a list of {"field": ..., "op": "==|!=|<|<=|>|>=|in|not-in| array-contains|array-contains-any", "value": ...} for anything beyond the date range (e.g. status, muscleGroup) — as many as needed, on any field. select_fields: return only these top-level fields per record (plus "_id") instead of the whole document — use this to fit more records under the response size cap when you don't need every field. start_after_id: pass the previous call's next_start_after_id to page past limit for a query that has more matching records than fit in one call or under the response size cap. Results are capped at config.MAX_RECORDS_PER_FETCH regardless of the requested limit — this is a personal read tool, not a bulk export pipe. The whole response is also subject to the server's ResponseLimiting middleware (config.MAX_RESPONSE_BYTES); an oversized response comes back truncated with a note rather than ballooning.

get_nutrition_goals

ChatGPT
The user's current daily nutrition targets (calories/protein/carbs/ fat), if they've ever been set. Call this before log_meal, or before any suggestion that touches diet, so you know whether real targets already exist rather than assuming. Returns {"set": False} if nothing has been saved yet. In that case, ask the user about their actual goal (losing fat, maintaining, or building muscle), activity level, and any dietary constraints, then call set_nutrition_goals with numbers grounded in that conversation — never invent numbers without asking first.

get_planned_workout

ChatGPT
Fetch one scheduled interval run/bike/swim plan by id, with its warmup/blocks/cooldown expanded into the same ordered, labeled segment list a completed workout's own measured actuals use (e.g. "Warmup", "Block 1 Work 1/4", "Block 1 Recovery 1/4", ..., "Cooldown") — so you can line up planned vs actual segment-by-segment just by matching label, without re-deriving the interval structure from the raw warmup/blocks/ cooldown fields yourself. workout_type: "run", "bike", or "swim". document_id: the id of the linked plan. A completed run/bike/swim record (from fetch_records, or list_run_workouts/list_bike_workouts/ list_swim_workouts for the scheduled side) carries this as linkedRunWorkoutId / linkedBikeWorkoutId / linkedSwimWorkoutId — only present if the user explicitly linked it when logging the workout in the app. If that field is missing on a completed record, there's nothing scheduled to compare it against. Right now only completed runs have a measured planned_segments actuals array to compare against (bike/swim segment-level actuals aren't built yet) — this tool itself already works for all three workout types, there's just nothing on the actual side yet for bike/swim to line up against it.

get_profile

ChatGPT
The connected user's own top-level profile: sex, stated training experience, and name. This is the one tool that reads the users/{uid} document itself rather than one of its subcollections — every other tool here is subcollection-scoped (lifts/run/bike/swim/ etc.) and can't reach these fields at all. There is no age, height, or weight field here, or anywhere else in this app right now. Never guess a value for any of these — if one would materially change an analysis (e.g. interpreting a heart-rate zone, or judging whether a load increase is reasonable for this person), ask the user directly instead of proceeding as if it's known. Any field below can also simply be unset for a given user; treat a missing field as "not provided," not an error — fall back to asking the user directly (e.g. for sex) if it's actually needed.

get_record

ChatGPT
Fetch one exact document by id. Pass select_fields to get back only those top-level fields if the full document might be large.

get_run_analysis

ChatGPT
Fetch the full rich analysis for up to 3 completed runs at once. This is the detailed tool, not the browsing one. fetch_records (collection="run") already returns every run's whole-workout averages (pace, HR, cadence, etc.) cheaply for scanning history or filtering by date — reach for this tool only once you already know which specific run(s) deserve a closer look, since the data here (mile-by-mile splits, scheduled-plan segment breakdowns, whole-run fatigue analysis) is heavier per run. For each run_id, returns: - mile_splits: one entry per completed mile (mile boundaries from real distance-crossing, not GPS points, and a trailing partial mile is left out rather than padded) — duration, pace, avg/min/max HR, HR drift, cadence, running power, elevation gain, and calories for that specific mile, not just its time. Useful for spotting whether a slow mile lines up with a hill, a HR spike, or a mechanics change, not just that it was slow. - planned_segments: per-scheduled-interval actual breakdown (duration, pace, HR, HR drift, cadence, power, elevation), only present if this run was linked to a scheduled plan when logged (linkedRunWorkoutId will be set). Pair this with get_planned_workout(workout_type="run", document_id=linkedRunWorkoutId) to compare planned vs. actual segment-by-segment by matching label. - fatigue_analysis: whole-run cardiovascular drift and running- mechanics comparison between the first and second half of the run (after trimming the first 10% as a warmup buffer). resolved: false with a reason means it couldn't be computed (e.g. run under 15 minutes, or no power/GPS pace data) — never fabricated. When resolved, hr_drift_pct is the efficiency-factor decoupling (positive = more heartbeats needed for the same effort later in the run, i.e. cardiovascular fatigue); basis says whether that efficiency factor used running power ("power", works indoor or out) or GPS pace ("pace", outdoor only, used only when power wasn't available). The *_delta_pct fields are plain percent change, second half vs. first — not a verdict on whether that direction is good or bad, just report it as an observation. Important: fatigue_analysis and planned_segments can be missing entirely (the key absent, not present-but-null) rather than showing resolved: false — that means this specific run was analyzed before that field existed, or hasn't been (re-)analyzed since, not that no fatigue/segments were found. Don't report "no fatigue detected" for a run missing the key outright; say the analysis hasn't been run for it and suggest the user re-run it from Profile -> Past Runs -> Enhance in the app (only runs with a stored HealthKit link show up there). run_ids: 1-3 document ids from the user's "run" collection (get these from fetch_records first — this tool does not search or list runs).

list_bike_workouts

ChatGPT
List cycling workouts this server has created for the user via schedule_bike_workout, most-recently-created first. status: optional filter — "pending" (not yet synced to Apple Fitness), "synced" (currently live on the user's Watch), or "deleted" (removed from Apple Fitness, kept here so the user can re-sync it later). Omit to see all of them. start_date / end_date: ISO 8601 date or datetime strings (e.g. "2026-09-01"), filtered against scheduledFor (when the ride is/was intended to happen), not when it was created. A bare date is inclusive on both ends, same convention as fetch_records. limit: capped at config.MAX_RECORDS_PER_FETCH regardless of what's requested, same as fetch_records. If you see a "pending" one that's days old, it likely just means the user hasn't opened Lion Lift and tapped Sync yet — mention that rather than assuming something went wrong.

list_collections

ChatGPT
List every subcollection that actually exists under this account right now (e.g. "lifts", "run", "bike", "swim", "core", plus anything else present) — discovered live from Firestore, not a hardcoded guess.

list_fields

ChatGPT
Sample a handful of real documents from collection and return every field name observed, each with one example value — Firestore has no fixed schema, so this is live discovery, not a spec. Use this before fetch_records if you're not sure what fields/dates a collection has.

list_known_exercises

ChatGPT
The app's own exercise name catalog (from Views/Constants/LiftsCatalog.swift, copied verbatim). Call this before schedule_lift so exercise names match what the app and its own autocomplete already use, instead of inventing new ones. Not enforced server-side — the app itself treats exercise names as free text — this is just here to keep things consistent.

list_meals

ChatGPT
Lists logged meals, most recent day first. Use this (plus get_nutrition_goals) before answering anything about diet, recovery, or whether nutrition might be affecting training — e.g. correlating a flat week of lifts with a stretch of under-eating, or a strong week with consistently hitting a protein target. start_date / end_date: "YYYY-MM-DD", inclusive on both ends, filtered against the day the meal was eaten (date_ymd) — not when it was logged. Omit either side for an open-ended range. limit: capped at config.MAX_RECORDS_PER_FETCH regardless of what's requested, same as fetch_records. Returns individual meals, not pre-aggregated daily sums — sum calories/protein_g/carbs_g/fat_g across a day's entries yourself to get that day's totals. Beyond the four required macros, a meal may or may not have any of log_meal's optional fields (fiber_g, sodium_mg, items, assumptions, nutrition_highlights, etc. — see log_meal's own doc comment for the full list) — whatever was actually estimated when it was logged. Treat an absent field as "not estimated," never as zero, when summing or reporting a total; say so explicitly rather than implying a known number for something nobody ever logged (e.g. "fiber: ~18g across 3 of 4 meals that had an estimate" rather than silently averaging in a 0 for the meal that didn't).

list_run_workouts

ChatGPT
List interval run workouts this server has created for the user via schedule_interval_run, most-recently-created first. status: optional filter — "pending" (not yet synced to Apple Fitness), "synced" (currently live on the user's Watch), or "deleted" (removed from Apple Fitness, kept here so the user can re-sync it later). Omit to see all of them. start_date / end_date: ISO 8601 date or datetime strings (e.g. "2026-09-01"), filtered against scheduledFor (when the run is/was intended to happen), not when it was created. A bare date is inclusive on both ends, same convention as fetch_records — end_date="2026-09-17" means through the end of that day. limit: capped at config.MAX_RECORDS_PER_FETCH regardless of what's requested, same as fetch_records. If you see a "pending" one that's days old, it likely just means the user hasn't opened Lion Lift and tapped Sync yet — mention that rather than assuming something went wrong.

list_swim_workouts

ChatGPT
List pool swim workouts this server has created for the user via schedule_pool_swim_workout, most-recently-created first. status: optional filter — "pending" (not yet synced to Apple Fitness), "synced" (currently live on the user's Watch), or "deleted" (removed from Apple Fitness, kept here so the user can re-sync it later). Omit to see all of them. start_date / end_date: ISO 8601 date or datetime strings (e.g. "2026-09-01"), filtered against scheduledFor (when the swim is/was intended to happen), not when it was created. A bare date is inclusive on both ends, same convention as fetch_records. limit: capped at config.MAX_RECORDS_PER_FETCH regardless of what's requested, same as fetch_records. If you see a "pending" one that's days old, it likely just means the user hasn't opened Lion Lift and tapped Sync yet — mention that rather than assuming something went wrong.

log_meal

ChatGPT
Logs one meal — typically right after being shown a photo of food and asked to estimate and save it. This server never sees the photo; whatever vision model is attached to this conversation does the actual recognition and estimation, and this tool just persists the result. IMPORTANT — confirm before calling: show the user your estimated breakdown (what you think the meal is, calories, protein/carbs/fat, and any of the optional detail below you're including) and get at least an implicit go-ahead before saving. These are visual estimates from a single photo, not a lab measurement, and can be meaningfully off for mixed dishes, sauces/oil, or portion size — say so if an estimate feels particularly uncertain rather than presenting it as exact. date: "YYYY-MM-DD", the day the meal was eaten — not necessarily today, if logging something from earlier. This is the field list_meals filters on; eaten_at below is a separate, more precise timestamp, not a replacement for this. meal_type: free text, but prefer "breakfast", "lunch", "dinner", or "snack" so the app's own UI groups these consistently. description: a short, human-readable description of what was eaten (e.g. "Grilled chicken breast, white rice, steamed broccoli"). calories / protein_g / carbs_g / fat_g: required, your best estimate for the whole meal — calories in kcal, the rest in grams. Everything below this point is optional, additive detail on top of these four. --- Everything from here down is optional. The rule that matters more than any individual field: never fill one in with a guessed/default number just because it's available — an unset field means "not estimated," and a 0 means "estimated at zero." Those are not the same thing, and the app treats them differently. If you don't actually have a reasonable basis for a value, leave it unset and use nutrition_highlights/assumptions (below) to say so in words instead. --- eaten_at: optional, ISO 8601 with a timezone offset (e.g. "2026-10-24T12:30:00-04:00") — the actual time the meal was eaten, if known. fiber_g / total_sugars_g / added_sugars_g / saturated_fat_g: optional, grams. added_sugars_g in particular depends on recipe/ingredient knowledge a photo alone usually can't give you — don't guess one just to fill the field. sodium_mg / potassium_mg / calcium_mg / iron_mg / magnesium_mg / vitamin_c_mg / vitamin_d_mcg: optional micronutrient estimates, in the units named. Only set one when you have an actual basis for it (a nutrition label, a known recipe, a restaurant's published data) — for a plain photo with nothing else to go on, it's usually more honest to leave these unset and use nutrition_highlights instead of inventing a number from appearance alone. fluid_ml: optional — the volume of a beverage, not an inferred water content of food. items: optional list of the individual foods/portions behind your estimate — name (required), quantity, unit ("g", "oz", "cup", "piece"), preparation ("grilled", "fried", "raw", etc.), and portion_source ("weighed", "user_reported", or "photo_estimated"). nutrient_provenance: optional, keyed by one of this meal's own nutrient field names (e.g. "fiber_g", "sodium_mg" — any key outside that set is rejected) — {source, confidence, reference} describing how reliable that particular number is. source is "nutrition_label", "restaurant_data", "food_database", "user_reported", or "assistant_estimate"; confidence ("low"/"medium"/"high") is qualitative, not a calibrated probability; reference names the actual product/database entry/URL used, if any. Only annotate a nutrient you're actually estimating. calorie_range: optional {low, high} instead of (or alongside) the single calories estimate, when a range is more honest than one number — low must be <= high. assumptions: optional list of short notes on what you assumed to reach these numbers (e.g. "whole bowl consumed", "no dressing assumed", "rice measured cooked") — record these whenever you actually made one, not just when asked. nutrition_highlights: optional list of short qualitative notes for anything worth mentioning that doesn't…

schedule_bike_workout

ChatGPT
Create a structured cycling workout — warmup, repeatable work/ recovery blocks, cooldown, each step with an optional heart-rate/ speed/power/cadence alert. Mirrors Apple's own WorkoutKit object model for cycling. IMPORTANT — confirm before calling: describe the full workout back to the user in plain language (warmup, every block's work/recovery and iteration count, any target speed/heart rate/power/etc., cooldown, and the scheduled date/time) and get their explicit confirmation first. This writes to their real account immediately and irreversibly from this server's own perspective — there's no "undo" tool. This does NOT put the workout on Apple Fitness/the Watch by itself — it only creates a pending record. WorkoutKit scheduling is a local, on-device operation this server has no way to perform remotely, so the user still has to open Lion Lift and tap "Sync" themselves (Workouts tab → Bike → Sync Workouts). Tell them that's the next step. There's no indoor/outdoor parameter here — the user picks trainer vs. road when they actually start the workout on the Watch, not at scheduling time. display_name: come up with a short, specific, memorable title based on what this workout actually is (e.g. "Sweet Spot Intervals", "Hill Repeats") — not a generic label like "Bike Workout" every time. Shown as this workout's title on the Watch/Fitness app and in Lion Lift's own Sync Workouts list, where several of these can be listed side by side. scheduled_for: "YYYY-MM-DDTHH:MM", 24-hour, the user's local time — when they intend to do this ride. Confirm "today"/"tomorrow" explicitly rather than assuming a timezone, same as schedule_lift. blocks: at least one BikeIntervalBlockInput. Each has: - iterations (int, default 1): how many times the work/recovery pair repeats. - work (required) / recovery (optional): each a BikeStepInput — see below. warmup / cooldown: optional single BikeStepInputs (no iteration count). A BikeStepInput is: {goal, alert?, display_name?}. - display_name: optional label for that one step, e.g. "Climb Repeat". - goal (a BikeGoalInput): {type, ...}. type is one of: "open" — no fields needed, the user advances it manually. "time" — needs seconds (float). "distance" — needs miles (float). "energy" — needs kilocalories (float). - alert (a BikeAlertInput, optional): {type, ...}. type is one of: "heartRateZone" — needs zone (int, e.g. 1-5). "heartRateRange" — needs bpm_low, bpm_high. "speedThreshold" — needs mph, optional metric ("average"/"current", default "average"). "speedRange" — needs mph_low, mph_high, optional metric. "powerZone" — needs zone (int). "powerRange" — needs watts_low, watts_high. "powerThreshold" — needs watts. "cadenceRange" — needs rpm_low, rpm_high (pedal RPM). "cadenceThreshold" — needs rpm (pedal RPM). Example — "5x5min hill repeats at 250-280W, 3 min easy spin recovery, with a 10 minute warmup and 10 minute cooldown": warmup = {"goal": {"type": "time", "seconds": 600}} blocks = [{ "iterations": 5, "work": { "goal": {"type": "time", "seconds": 300}, "alert": {"type": "powerRange", "watts_low": 250, "watts_high": 280}, "display_name": "Climb Repeat", }, "recovery": {"goal": {"type": "time", "seconds": 180}, "display_name": "Spin Recovery"}, }] cooldown = {"goal": {"type": "time", "seconds": 600}}

schedule_interval_run

ChatGPT
Create a structured interval running workout — warmup, repeatable work/recovery blocks, cooldown, each step with an optional pace/heart- rate/power/cadence alert. Mirrors Apple's own WorkoutKit object model field-for-field (CustomWorkout/WorkoutStep/IntervalBlock/WorkoutGoal/ WorkoutAlert). IMPORTANT — confirm before calling: describe the full workout back to the user in plain language (warmup, every block's work/recovery and iteration count, any target pace/heart rate/etc., cooldown, indoor vs outdoor, and the scheduled date/time) and get their explicit confirmation first. This writes to their real account immediately and irreversibly from this server's own perspective — there's no "undo" tool. This does NOT put the workout on Apple Fitness/the Watch by itself — it only creates a pending record. WorkoutKit scheduling is a local, on-device operation this server has no way to perform remotely, so the user still has to open Lion Lift and tap "Sync" themselves (Workouts tab → Run → Sync Workouts). Tell them that's the next step. display_name: come up with a short, specific, memorable title based on what this workout actually is — e.g. "50m Sprint Repeats", "Tempo Ladder", "5x800 Negative Split" — not a generic label like "Interval Run" every time. Shown as this workout's title on the Watch/Fitness app and in Lion Lift's own Sync Workouts list, where several of these can be listed side by side, so it needs to read as distinct from the user's other workouts at a glance, not just descriptive in isolation. scheduled_for: "YYYY-MM-DDTHH:MM", 24-hour, the user's local time — when they intend to do this run. Confirm "today"/"tomorrow" explicitly rather than assuming a timezone, same as schedule_lift. location: "outdoor" (default, GPS-based — required for any pace/speed alert) or "indoor" (treadmill — pace alerts aren't meaningful without GPS; use a heart-rate or cadence alert instead for an indoor workout). blocks: at least one IntervalBlockInput. Each has: - iterations (int, default 1): how many times the work/recovery pair repeats. - work (required) / recovery (optional): each a StepInput — see below. warmup / cooldown: optional single StepInputs (no iteration count). A StepInput is: {goal, alert?, display_name?}. - display_name: optional label for that one step, e.g. "800m Repeat". - goal (a GoalInput): {type, ...}. type is one of: "open" — no fields needed, the user advances it manually. "time" — needs seconds (float). "distance" — needs miles (float). "energy" — needs kilocalories (float). - alert (an AlertInput, optional): {type, ...}. type is one of: "heartRateZone" — needs zone (int, e.g. 1-5). "heartRateRange" — needs bpm_low, bpm_high. "paceThreshold" — needs pace_sec_per_mile, optional metric ("average"/"current", default "average"). "paceRange" — needs pace_sec_per_mile_low, pace_sec_per_mile_high (lower = faster), optional metric. "powerZone" — needs zone (int). "powerRange" — needs watts_low, watts_high. "powerThreshold" — needs watts. "cadenceRange" — needs spm_low, spm_high (steps/min). "cadenceThreshold" — needs spm (steps/min). Example — "5x800m at 6:30-7:00/mile pace, 2 min jog recovery, with a 10 minute warmup and 5 minute cooldown": warmup = {"goal": {"type": "time", "seconds": 600}} blocks = [{ "iterations": 5, "work": { "goal": {"type": "distance", "miles": 0.5}, "alert": {"type": "paceRange", "pace_sec_per_mile_low": 390, "pace_sec_per_mile_high": 420}, "display_name": "800m Repeat", }, "recovery": {"goal": {"type": "time", "seconds": 120}, "display_name": "Jog Recovery"}, }] cooldown = {"goal": {"type": "time", "seconds": 300}}

schedule_lift

ChatGPT
Schedule a new lift workout. This is the only write this server can do, deliberately narrow in scope — create-a-scheduled-lift only, never update/complete/delete. Mirrors exactly what the app itself writes when scheduling a workout (ManualWorkoutEntryView.saveWorkout() / LiftCompletionService.complete()), so a workout scheduled here behaves identically to one scheduled from the app — shows up in today's-workouts queries, syncs to the watch on its next check-in, etc. IMPORTANT — confirm before calling: always show the user the exact workout you're about to schedule (date, every exercise, every set's weight/reps, and notes if any) and get their explicit confirmation first. This tool writes to their real, live workout schedule immediately and irreversibly from its own perspective (there's no "undo" tool) — never call it speculatively or as part of just thinking out loud with the user. scheduled_date: a plain calendar date, "YYYY-MM-DD" (e.g. "2026-09-20") — not a datetime, since scheduling is a whole-day concept. Confirm "today" with the user rather than assuming a timezone if the date is ambiguous (e.g. requested near midnight) — this server has no configured local timezone to resolve that for you. Every other date-shaped field the app relies on (the display string, date_ymd, date_ts, weekId) is derived from this single input, matching exactly what the app's own code computes for it — status is always "scheduled" and created_at is always "now," neither is a parameter. lifts: exercise name + sets (weight in lbs, reps) per exercise — call list_known_exercises() first for real exercise names instead of inventing new ones. Muscle group(s) are auto-classified from these exercise names, exactly like the app itself now does — there's no muscle_group parameter to set. notes: optional, defaults to empty. Once this lift is actually completed, its lifts field gets overwritten with the real final sets — the planned weight/reps you pass here stop being visible there. A frozen copy of what you scheduled survives separately in original_lift_plan (set automatically, nothing to pass for it), so a completed lift can later be compared planned-vs-actual the same way it works for scheduled runs/swims/rides.

schedule_pool_swim_workout

ChatGPT
Create a structured POOL swimming workout — warmup, repeatable work/recovery blocks, cooldown, each step with an optional heart-rate/ power/cadence alert and an optional stroke-type label — mirroring Apple's own WorkoutKit object model for pool swimming specifically (open-water swimming isn't supported by this tool). IMPORTANT — confirm before calling: describe the full workout back to the user in plain language (warmup, every block's work/recovery and iteration count, any stroke types, any target heart rate/etc., cooldown, and the scheduled date/time) and get their explicit confirmation first. This writes to their real account immediately and irreversibly from this server's own perspective — there's no "undo" tool. This does NOT put the workout on Apple Fitness/the Watch by itself — it only creates a pending record. WorkoutKit scheduling is a local, on-device operation this server has no way to perform remotely, so the user still has to open Lion Lift and tap "Sync" themselves (Workouts tab → Swim → Sync Workouts). Tell them that's the next step. Pool length and pool-vs-open-water are deliberately not parameters here — Apple's own Workout app asks the swimmer for pool length and location when they actually start the workout, and auto-scales distances/times to match, so there's nothing for this tool to set. display_name: come up with a short, specific, memorable title based on what this workout actually is (e.g. "Threshold 100s", "IM Ladder") — not a generic label like "Swim Workout" every time. Shown as this workout's title on the Watch/Fitness app and in Lion Lift's own Sync Workouts list, where several of these can be listed side by side. scheduled_for: "YYYY-MM-DDTHH:MM", 24-hour, the user's local time — when they intend to do this swim. Confirm "today"/"tomorrow" explicitly rather than assuming a timezone, same as schedule_lift. blocks: at least one SwimIntervalBlockInput. Each has: - iterations (int, default 1): how many times the work/recovery pair repeats. - work (required) / recovery (optional): each a SwimStepInput — see below. warmup / cooldown: optional single SwimStepInputs (no iteration count). A SwimStepInput is: {goal, alert?, display_name?}. - display_name: optional label for that one step — either descriptive (e.g. "Kick Set") or the stroke type (e.g. "Freestyle", "Backstroke", "Breaststroke", "Butterfly", "IM"). - goal (a SwimGoalInput): {type, ...}. type is one of: "open" — no fields needed, manual advance. "time" — needs seconds (float). "distance" — needs meters (float). "energy" — needs kilocalories (float). "poolSwimDistanceWithTime" — needs BOTH meters and seconds — a combined distance-and-time target (e.g. "100m within 1:30"); the step only advances once both are met, not just whichever comes first. - alert (a SwimAlertInput, optional): {type, ...}. No pace/speed option — there's no GPS in a pool. type is one of: "heartRateZone" — needs zone (int, e.g. 1-5). "heartRateRange" — needs bpm_low, bpm_high. "powerZone" — needs zone (int). "powerRange" — needs watts_low, watts_high. "powerThreshold" — needs watts. "cadenceRange" — needs spm_low, spm_high (strokes/min). "cadenceThreshold" — needs spm (strokes/min). Example — "10x100m freestyle on a 1:30 interval, with a 200m warmup and 200m cooldown": warmup = {"goal": {"type": "distance", "meters": 200}} blocks = [{ "iterations": 10, "work": { "goal": {"type": "poolSwimDistanceWithTime", "meters": 100, "seconds": 90}, "display_name": "Freestyle", }, }] cooldown = {"goal": {"type": "distance", "meters": 200}}

set_nutrition_goals

ChatGPT
Sets (or updates) the user's daily nutrition targets — what the app's dashboard fill bars measure logged meals against. IMPORTANT — ask before calculating: these numbers should come from an actual conversation with the user about their goal, activity level, and dietary preferences, not a formula applied silently to whatever stats happen to be on file (this server doesn't even have reliable age/height/weight to run one — see get_profile's own doc comment). Call get_nutrition_goals first; if targets already exist, confirm with the user before overwriting them rather than assuming they want a change. calories: daily target, kcal. protein_g / carbs_g / fat_g: daily targets, grams. commentary: free text — briefly note the reasoning (e.g. "maintenance calories for a lifter recomping, moderate-protein split") so a later conversation can see why these particular numbers were chosen. This is a single standing target, not tied to any specific week — call this again later with new numbers whenever the goal changes.

Capabilities

Writes

Example Prompts

Click any prompt to copy it.

Similar Apps