fitatu-api
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| FITATU_SESSION | No | Path to the Fitatu session file used by the MCP server. Falls back to $FITATU_SESSION_PATH, then ~/.fitatu/session.json (written by `fitatu-api login`). | |
| FITATU_LOG_LEVEL | No | Log level for the library, one of DEBUG, INFO, WARNING, ERROR. Logs go to stderr, never stdout; defaults to WARNING. | |
| FITATU_SESSION_JSON | No | The session JSON itself, instead of a session file; takes precedence over any file. Useful where no writable session file fits (containers, cron). Produced by `fitatu-api login --print` or `fitatu-api import-session --print`. | |
| FITATU_SESSION_PATH | No | Alternative to $FITATU_SESSION: path to the Fitatu session file used when $FITATU_SESSION is not set; otherwise ~/.fitatu/session.json is used. | |
| FITATU_CLIENT_PROFILE | No | JSON object with the keys of WEB_CLIENT_PROFILE to change (e.g. '{"app_version": "4.14.0"}') when Fitatu changes what its web app sends before a release of this library follows. Any other key or a non-plain-text value is refused and fitatu-mcp then does not start. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| get_todayA | Today's date in the user's time zone: date (YYYY-MM-DD), timezone and weekday. Call it when the user says today, yesterday, tomorrow, this week or a weekday, and count from it. |
| get_dayA | Read one diary day: per meal its meal_key (the slot other tools take), meal_name (the name the user gave it) and every logged food (row_id, food_type, product_id or recipe_id, name, measure_id, measure_name and measure_quantity, weight in g, energy in kcal, protein/fat/carbohydrate/fiber/sugars/salt in g, eaten); per-meal and day totals; and logged activities (row_id, name, energy in kcal burned); note, the user's own text for the day (null without one), and tags_ids, the ids of its note tags. What the user ate is eaten_totals (rows ticked as eaten); when it is 0 but totals is not, the rows are logged without the tick, so use totals and say so. totals counts every row, planned or eaten. Burned energy is not subtracted. Read the day first to get the row_ids that update_item, move_item and remove_items take. A PRODUCT or RECIPE row's product_id or recipe_id is the food_id that add_food takes, with its food_type, measure_id and measure_quantity as quantity. |
| get_targetsA | Read the user's daily goals for a day: energy in kcal, protein/fat/carbohydrate as grams and percent of energy, and BMR/TMR in kcal. date is the day asked for; settings_since is when these goals took effect (an earlier day is normal). |
| get_profileA | Read what the user's goals are computed from, for advice: height_cm, weight_kg (the current weight in the profile; a weigh-in logged for today updates it), start_weight_kg and target_weight_kg, bmi (from height_cm and weight_kg), goal (lose, maintain or gain) with pace_kg_per_week, pal (the activity level: TMR = BMR x pal), sex (male, female or null), age in years, and premium: active (whether the account has Fitatu Premium) and end_date. kg and cm whatever units the user chose in the app; a value is null when the profile lacks it. The daily goals are get_targets, the weigh-ins get_weight_history. |
| get_range_summaryA | Read Fitatu's own summary of a date range, as the app's Summary tab shows it: nutrition (energy in kcal; protein, fat, carbohydrate, fiber, sugars, salt in g) counts every logged row, ticked as eaten or not, like get_day's totals; eaten_nutrition the same for the rows ticked as eaten only; activity_energy is kcal burned. Both are totals over the whole range, not daily averages: divide by the number of days for one. For goals and what is left per day, use get_progress. |
| get_progressA | Progress against the user's goals, day by day, for one day or a range of up to 62 days: per day and for energy, protein, fat and carbohydrate, logged (every row, planned or eaten; null when nothing is logged that day), the goal (target in kcal for energy; target_min and target_max in g for a macro, which is on target between the two) and left (the goal minus logged, a macro's from target_max; negative when over). totals gives the whole range: logged, eaten (only rows ticked as eaten) and the goal summed over the days. Works on every account; without Fitatu Premium it reads each day on its own, so it covers at most 31 days, and each day has eaten too. Answers 'what is left today', 'did I hit protein' and how each day went; a summary of a week or month with weight and top foods, or of a longer range, is get_report (over 31 days without Fitatu Premium, get_range_summary). |
| get_top_foodsA | The foods that gave the user the most of a nutrient over a date range, most first: name, amount (kcal for energy, g otherwise, summed over the range) and per100 (the food's own amount per 100 g; null for a custom item). A recipe may be listed as its ingredients. Answers 'where does my sugar come from'. Premium only: on an account without Fitatu Premium, Fitatu refuses it (a forbidden error); tell the user, do not retry. |
| get_reportA | A weekly or monthly report (or one of any range) in one call. nutrition: for energy, protein, fat and carbohydrate the range's totals, logged (every row, planned or eaten) and eaten (rows ticked as eaten), and daily: logged averaged over days_logged (the days with anything logged) next to the average daily goal (target for energy, target_min and target_max for a macro). weight: the first and last weigh-in of the range (date, value in the user's weight unit) and the change; null without one. top_foods: the 5 foods with the most energy and the 5 with the most protein, as get_top_foods gives them. Without Fitatu Premium, top_foods is null (top_foods_note says why) and the range is at most 31 days; get_range_summary gives the totals of a longer one. |
| get_shopping_listA | A shopping list for a range of up to 14 days, worked out from the rows planned in the diary and not ticked as eaten. products sums them per product and measure (product_id, name, measure_name, quantity of that measure, weight_g; weight_g is null when a row does not give it), with the ingredients of each planned recipe added for its portions; recipes lists those recipes (date, meal, recipe_id, name, portions, and expanded false when its ingredients could not be read or it is a premium recipe on an account without Fitatu Premium, so they are missing from products); other_items lists custom and other rows as they are. It cannot know what the user already has at home, pack sizes or anything not planned in the diary: say so. Start at today ("this week" is today to Sunday): rows on past days not ticked as eaten were usually eaten anyway. One request per day and one per recipe. |
| search_foodA | Search the Fitatu food catalog (Polish names work best). Each hit has food_id and type (PRODUCT or RECIPE) for add_food and get_food_measures, name, brand, its default measure (measure_id, name, weight_g or capacity_ml of one measure) with energy_kcal for one such measure, times_used (how often the user logged it; null when the server does not say) and favorite (whether it is one of the user's favourites). A barcode belongs in find_by_barcode. With locale, the hits come from that country's catalog (foods the user logged before show up too) and can be logged like any other. |
| search_recipesA | Search the Fitatu recipe catalog for meal ideas, e.g. phrase 'kurczak' with max_energy_kcal 500 for a chicken dinner of at most 500 kcal, or diets ['vegan'], meal 'breakfast' and max_cooking_time 10 for a quick vegan breakfast. The filters are per portion and all apply together; all but max_energy_kcal need Fitatu Premium (without it a forbidden error: tell the user, search by phrase and max_energy_kcal instead). Each hit is shaped like a search_food hit of type RECIPE (food_id, name, energy_kcal and measure of one portion) plus cooking_time and preparation_time as the catalog gives them, rating, and premium: whether the app keeps the recipe for Fitatu Premium (get_recipe and get_food_measures read it only with Premium). get_recipe gives a hit's ingredients and steps. Log one with add_food: food_type RECIPE, quantity the portions. |
| get_recipeA | Read one recipe in full, from the catalog or the user's own: name, servings (how many servings the ingredients make), ingredients for the whole recipe (food_id, food_type, quantity, measure_id, name, measure_name and weight_g; create_recipe and update_recipe take only the first four: drop the rest), description (the steps), per_serving (its portion measure as get_food_measures lists it: weight_g, energy_kcal, protein_g, fat_g and carbohydrate_g of one serving; null when it has none), cooking_time and preparation_time in minutes as the catalog gives them, meals (the meal types it suits), tags (e.g. vegan, keto, polish), rating, premium, deleted, and own: whether it is the user's private recipe, which update_recipe and delete_recipe take. A premium recipe needs Fitatu Premium: without it a forbidden error. |
| find_by_barcodeA | Find the catalog product with a barcode (the EAN or UPC digits printed under the bars), or the user's own product with it (create_food's barcode). Returns it in the same shape as a search_food hit, for add_food and get_food_measures. A barcode that no product has is not_found: then search_food by the product's name. |
| get_food_measuresA | List the measures a food can be logged in: measure_id, name (e.g. 'g', 'portion', 'piece'), weight_g or capacity_ml, energy_kcal and protein_g, fat_g, carbohydrate_g of one measure (a macro is null when the food's details do not give it; a product the user created with unit ml gives a serving's ml as weight_g). Pass measure_id and a quantity of that measure to add_food. A premium recipe needs Fitatu Premium: without it a forbidden error. |
| get_recent_foodsA | List foods the user recently logged in a meal, most used first, in the same shape as search_food hits. Handy for 'my usual breakfast'. |
| get_favorite_foodsB | List the user's favourite products and recipes, in the same shape as search_food hits. |
| get_my_foodsA | List the foods the user created, their own recipes (create_recipe) and products (create_food), in the same shape as search_food hits: a RECIPE hit's food_id is what update_recipe and delete_recipe take, a PRODUCT hit's what update_food and delete_food take. |
| get_weight_historyA | List logged body weights as {date, value, difference, direction}, newest first: value in the user's weight unit (kg unless changed in the app), difference from the previous entry, direction the weight goal (1 gain, -1 lose, 0 keep). Pass from_date and to_date to choose the period; without them it lists the newest entries up to limit, however old (there is no default period). For the change over a long period, get_report. |
| get_body_measurementsA | Read how the user's body measurements changed over a period: per body_part (neck, chest, waist, stomach, hips, thigh, calf, biceps, fat_percentage) the first and last value of the period (start_value, end_value) and the difference. Sizes are in the user's size unit (cm unless changed in the app), fat_percentage in %. Parts with nothing logged are left out. Pass from_date and to_date to choose the period; without them the server picks it. Body weight is get_weight_history. |
| get_measurement_historyA | List the logged values of one body measurement as {date, value}, newest first: value in the user's size unit (cm unless changed in the app), fat_percentage in %. Days without that measurement are left out. Pass from_date and to_date to choose the period; without to_date it stops at today, so give it for later days; without either it lists the newest entries up to limit. The first and last value of every part over a period is get_body_measurements; body weight is get_weight_history. |
| get_waterA | Read how much water the user drank on a day (water_ml), the day's goal (target_ml) and the size of one glass in the user's settings (glass_ml); each is null when unknown. |
| get_fastingA | Read the user's intermittent fasting (post przerywany) for a range of up to 62 days: days lists each day with fasting, its eating_periods (when the user ate, from and to as times of day) next to expected (the eating windows of the scheme); a day without fasting is not listed. scheme is the fasting scheme in the user's settings (type, e.g. 16_08_hours or 5_2_days, week_days and start_date as Fitatu gives them), null when none is set. |
| get_habitsA | Read the habits the user took up in the app (e.g. drinking water every day): per habit its habit_id, name, length_days (how long it runs), days_done and checked_days (the days ticked), enrolled_date and completed. available lists the other habits the app offers (habit_id, name, length_days, premium). |
| get_good_daysA | List the days the app's summary marks as good days (Fitatu's own rule decides which), oldest first, with their count. Pass from_date and/or to_date to keep only a period; without them, every good day there is. |
| search_activityA | Search the activity (workout) catalog, e.g. 'bieganie' (running) or 'rower' (cycling). Each hit has activity_id for add_activity, name, and energy_kcal: the catalog's estimate for the effort in energy_for (e.g. '5 km w 00:30:00') for an average person. Scale it to what the user did to get the energy_kcal that add_activity needs. |
| preview_removalA | Show what remove_items would delete, without deleting anything: would_remove lists each food row with what it is (meal, meal_name, row_id, name, food_type, food_id, measure_id, measure_name, measure_quantity, energy in kcal, protein/fat/carbohydrate in g, eaten), as remove_items' removed does; missing lists the ids that are not food rows on the day. data.ok is false when one is missing. Call it before remove_items and name the foods to the user, not their row_ids. |
| add_foodA | Log a catalog food in a meal; returns its row_id. food_id and food_type come from a search_food, search_recipes, find_by_barcode, get_recent_foods, get_favorite_foods or get_my_foods hit, create_food's food_id (PRODUCT) or create_recipe's recipe_id (RECIPE). For a PRODUCT give either unit 'g' or 'ml' with quantity in grams or millilitres, or measure_id (from the hit or get_food_measures) with quantity = how many of that measure. A RECIPE is logged in portions unless measure_id names another of its measures; unit is for products only. A premium recipe needs Fitatu Premium: without it a forbidden error. A measure_id the food does not have is refused before anything is written (that check reads the food once), and the error lists its measures. Each call adds a row unless row_id is reused. The row is not read back: data.ok says the sync accepted it, so read get_day to check what was logged. The result names the measure the row was stored in (measure_id, measure_name, measure_quantity): grams of a food without a gram measure are stored as a count of another one (70 g as 2 portions of 35 g), and update_item's quantity counts that measure. |
| add_foodsA | Log several catalog foods in one meal with one write, e.g. a whole breakfast. Each item takes food_id, food_type, quantity and measure_id or unit as add_food does, and is checked the same way before anything is written (one read per item with measure_id or unit): when one is wrong, nothing is logged and the error names it as items[index]. Returns the rows (row_id, food_id, measure_id, measure_name, measure_quantity) in the order of items; they are not read back, data.ok says the sync accepted them. Each call adds rows unless row_id is reused. |
| add_custom_itemA | Log a food that is not in the catalog, by name and the nutrition of the whole amount eaten; returns its row_id. Each call adds a row unless row_id is reused. When the user gives only kcal, estimate protein_g, fat_g and carbohydrate_g for that food and tell the user they are estimates; an omitted one counts as 0 g. |
| update_itemA | Change a logged food row: its quantity (in the row's own measure, see measure_name and measure_quantity in get_day) and/or eaten. Nothing else can be changed: for another food, or a custom row's name or nutrition, remove the row and add it again. Activities cannot be updated: remove and add them again. data.ok says a re-read shows the change; data.previous is the row's quantity and eaten before it, to undo the change. |
| move_itemA | Move a logged food row to another meal and/or day. The moved row gets a new row_id, returned here; the old one is gone. data.ok says a re-read confirms both. |
| copy_mealA | Copy the food rows of a meal, or only row_ids of it, to another meal and/or day in one write, e.g. 'the same breakfast as yesterday' or 'plan this lunch for tomorrow'. Products and recipes keep their measure and quantity, custom rows their name and nutrition; rows of other types are not copied and are listed in skipped. Returns the copies' row_ids (remove_items with them undoes the copy); data.ok says a re-read shows them all. Each call adds rows unless row_id is reused. |
| remove_itemsA | Delete logged food rows from a day by row_id (meals[].items[].row_id in get_day). removed lists each deleted row with what it was (meal, meal_name, row_id, name, food_type, food_id, measure_id, measure_name, measure_quantity, energy in kcal, protein/fat/carbohydrate in g, eaten), enough to add it again: with add_food, or with add_custom_item from its name and nutrition when food_id is null (a custom row); failed lists the rows still there. Ids that are not food rows on the day (already deleted, or an activity's row_id) are listed in missing, so repeating the call is safe. Preview a removal with preview_removal, which only reads; dry_run does the same here. data.ok is false when a row was missing or failed; the result's top-level ok only says the call ran. |
| log_weightA | Save the user's body weight for a day in kg, replacing a weight already saved for that day; other body measurements of that day are kept. data.ok says the server accepted it. |
| log_measurementsA | Save body measurements for a day: sizes in the user's size unit (cm unless changed in the app), fat_percentage in %. Only the values given are replaced; the day's other measurements and its weight are kept. Body weight is log_weight. data.ok says the server accepted it. |
| set_waterA | Set the total water drunk on a day in ml. It replaces the day's total instead of adding to it: to add some, read get_water and send the sum. For glasses, multiply by get_water's glass_ml (ask the user for the size when it is null). data.ok says the server accepted it. |
| set_targetsA | Change the user's daily goals from today on; earlier days keep theirs. energy_kcal sets a manual calorie goal. protein_pct, fat_pct and carbohydrate_pct set the macro split as whole percent of the energy, all three summing to 100; the grams follow from the energy. With energy_kcal alone the current split stays, or 20/30/50 when the account has none (say so); the percentages alone keep the current energy, as a manual goal. automatic true goes back to the goal Fitatu calculates from the user's profile, with no energy_kcal or percentages. water_glass_ml is the glass size water is counted in (get_water's glass_ml). Returns the goals now in effect as get_targets gives them, plus automatic, glass_ml and previous: the arguments that put back what this call changed, with no water_glass_ml when there was no glass size (that cannot be put back). data.ok says Fitatu's reply shows the change. |
| set_noteA | Replace the user's note for a diary day (get_day's note) with note; an empty note deletes it. The day's note tags are kept, and so is everything else of the day. data.previous is the note before ('' when there was none), to put it back; data.ok says a re-read shows the new note. |
| add_activityA | Log a workout from the activity catalog; returns its row_id. Fitatu stores energy_kcal as given and does not compute it (see search_activity). Each call adds an activity unless row_id is reused. data.ok says a re-read shows it. |
| remove_activityA | Delete a logged activity by row_id (activities[].row_id in get_day, or from add_activity). An id that is not an activity on the day (already deleted, or a food row's row_id) gives data.ok false, reason item_not_found and a message listing the day's activities, so repeating the call is safe; the result's top-level ok only says the call ran. |
| set_favoriteA | Add a catalog food to the user's favourites, or remove it with favorite false. food_id and food_type come from search_food, get_recent_foods or get_favorite_foods. Repeating it is safe. The server applies it asynchronously, so get_favorite_foods may not show the change for a moment. data.ok says the server accepted it. |
| create_recipeA | Save a private recipe in the user's own foods from catalog products, e.g. their usual smoothie, so it can be logged as one food. Only the user sees it; it never goes to the shared catalog. Each ingredient takes food_id, quantity and unit or measure_id as add_food does, is a PRODUCT, and is checked the same way before anything is written: when one is wrong, nothing is created and the error names it as ingredients[index]. Returns recipe_id and per_serving, its portion measure as get_food_measures lists it (weight_g, energy_kcal, protein_g, fat_g, carbohydrate_g of one serving; null when the recipe could not be read back). Log it with add_food: food_id recipe_id, food_type RECIPE, quantity the servings eaten. Each call creates another recipe: when one fails with anything but a validation, not_found or rate_limit error, look for it with get_my_foods before creating it again. |
| update_recipeA | Change one of the user's own private recipes (create_recipe's recipe_id or a get_my_foods RECIPE hit's food_id): its name, ingredients (the whole new list, each as create_recipe takes it and checked the same way), servings and/or description; what is not given stays as it is. Any other recipe is refused before anything is changed (that check reads the recipe). Returns per_serving as create_recipe does, and previous (name, servings, description, ingredients), which update_recipe takes to undo the change. data.ok says the server accepted it. |
| delete_recipeA | Delete one of the user's own private recipes: create_recipe's recipe_id or a get_my_foods RECIPE hit's food_id. Any other recipe (a catalog recipe, a shared one or another user's) is refused before anything is changed (that check reads the recipe). Fitatu keeps a deleted recipe marked deleted, out of the user's foods and searches; deleting it again gives data.ok true with already_deleted true. |
| create_foodA | Save a product in the user's own foods from its nutrition label, e.g. a bar the catalog does not have, to log it by weight or by its servings. Only the user sees it; it never goes to the shared catalog. Values are per 100 g (100 ml with unit ml), as the label gives them: when it gives only a serving's, scale them to 100 g. Energy in kcal, not kJ. Returns food_id and measures, its measures as get_food_measures lists them (null when it could not be read back). Log it with add_food: food_id, food_type PRODUCT, and unit g (ml) with the amount, or a measure_id with the count. Each call creates another product: when one fails with anything but a validation or rate_limit error, look for it with get_my_foods before creating it again. |
| update_foodA | Change one of the user's own products (create_food's food_id or a get_my_foods PRODUCT hit's food_id): any value create_food takes but unit; what is not given stays as it is, and servings replaces the list. Any other product is refused before anything is changed (that check reads it). Fitatu usually changes the product in place (replaced_food_id null), and rows already logged with it then show the new values too. It may save the change as a new product instead (seen after a rename or new servings): food_id is then the new id and replaced_food_id the old, retired one, and rows logged before keep the old values. Before changing a product the user has logged, tell them their logged rows may change. Returns measures as create_food does and previous, which update_food takes on food_id to undo the change. data.ok says the server accepted it. |
| delete_foodA | Delete one of the user's own products: create_food's food_id or a get_my_foods PRODUCT hit's food_id. Any other product (a catalog one or another user's) is refused before anything is changed (that check reads it). Rows already logged with it keep their name and values. Fitatu keeps a deleted product marked deleted, out of the user's foods and searches; deleting it again gives data.ok true with already_deleted true. previous holds what create_food takes to make it again (under a new food_id). |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
| podsumowanie_dnia | Summarize a diary day: what was eaten against the goals, what is left, the meals and water. |
| przeglad_tygodnia | Review this week, Monday to today: daily averages against the goals, the days that stood out, the weight change and, with Fitatu Premium, the top foods. |
| zaloguj_posilek | Log a meal from a description, e.g. 'dwa jajka i kawa z mlekiem na śniadanie'. |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 47 tools
Most tools target a clearly distinct resource and action (add_food vs add_foods vs add_custom_item vs add_activity, get_day vs get_progress), and the descriptions explicitly cross-reference the right sibling tool. However, the report family (get_range_summary, get_progress, get_report, get_top_foods) and the measurement family (get_weight_history, get_body_measurements, get_measurement_history) overlap enough that only the very long descriptions prevent misselection.
Every name is snake_case with a predictable verb_noun shape: get_*, add_*, update_*, remove_*, set_*, create_*, delete_*, search_*, find_*, move_*, copy_*, log_*, preview_*. No camelCase or vague verbs break the pattern.
At 47 tools this is heavy, and several reads (three summary/report tools, three measurement-history tools) add surface redundancy. It is defensible given the breadth of the Fitatu domain (diary, foods, recipes, activities, body data, water, fasting, habits, goals, notes, reports), but it sits well above the comfortable 3-15 range.
Coverage is unusually thorough: full CRUD for user foods and private recipes, add/remove for diary rows and activities, reads and writes for weight, measurements, water, targets, notes and favourites, plus search, barcode lookup, meal copy/move, shopping list and multiple report views. No obvious lifecycle gap for the stated domain.