№ 530309September 2026 · Malibu, CA
Lift
A workout tracker that AI can program and read back.Problem
When Claude programs my weight-lifting schedule and details, it does not have my exercise history or information on my sets, reps, and weights. Without this information, Claude cannot effectively select the right schedule, program, and exercises.
Solution
Claude writes my lifts and they render in a beautiful UI so I can input my weights and reps, and change exercises, order, etc. All of this is easily accessible via MCP to Claude when programing the next one. (This lives as an “app” in FinallyNote. I imagine I will find use for creating more “apps” that visualize and interact with my AIs of choice).
Suggested versus actual, per set
The AI proposes numbers in gray; only what I actually lifted counts as history.
Each set row in SQLite carries two pairs of numbers, reps and weight_lb for what I lifted and suggested_reps and suggested_weight_lb for the plan, plus a done_at timestamp. The MCP planning tools, lift_plan_workout and the add_sets and set_suggestions fields of lift_update_workout, can only reach the suggested columns; their Zod schema maps whatever an AI sends straight into them. Actuals are written by set.patch when I fill a field, or by record_sets when I explicitly tell the AI what I lifted. Marking a set done with empty actuals copies the suggestion across, so one tap accepts a plan, and the first done set flips a planned workout to in_progress. History and the Epley one-rep max only count sets with done_at.
The programming loop over MCP
An AI reads my whole history in one call, plans the next workout, and I just show up.
Lift registers its tools on the same MCP server as the notebook, so they exist on stdio and on the OAuth endpoint alike. lift_dump returns the complete, unpaginated history as JSON: every set with actual, suggested and done, custom exercises, and the daily routine with per-day adherence and notes. That is deliberate; a coach should load everything before programming. Writes go through lift_plan_workout and lift_update_workout, which resolve an exercise by id, exact name or alias, refuse an unknown name with close matches rather than creating junk, and apply each call in one SQLite transaction. A short reference document explains the data model without prescribing how to coach. Every call is logged, and the planned workout waits on the home screen.
Offline at the gym
Every tap is saved on the phone first and sent when there is signal, so a set is never lost.
Every mutation is a LiftOp with a client-generated op_id, pushed onto a queue persisted in localStorage and applied at once to the React Query cache by pure reducers that mirror the server's rules, so the screen never waits on the network. A flusher posts batches to /api/lift/ops with exponential backoff, and fires again when the phone comes back online. The server remembers applied op_ids for thirty days and answers 'duplicate' to a replay, so a lost acknowledgement is harmless. Consecutive patches to one row coalesce into a single op, but never into one already on the wire, since its ack would erase the newer change. Rejected ops land in a small dead list rather than blocking the queue.

