
Fitatu
io.github.Jezue由 GitHub 帳號 Jezue 發布v0.6.0更新於 Oct 24, 2026
Unofficial Fitatu calorie counter: read and log food, weight, water and workouts.
概覽
非官方的 Fitatu 熱量記錄 MCP 伺服器,讓助理讀取並記錄使用者本人的飲食、體重、飲水與運動資料。
- 可以做什麼
- 讀取某天的飲食日記,搜尋食物、食譜與條碼,並新增、修改、移動、複製或刪除日記項目與備註
- 搜尋並新增或移除活動與運動紀錄
- 讀取體重與身體測量歷史,記錄體重與測量值,讀取與設定飲水量
- 讀取與設定目標、個人檔案、進度、區間彙總、報告與購物清單
- 管理使用者自己的私人食物與食譜、收藏與最近食物
- 讀取斷食方案、習慣與達標日
- 適用情境
- 適合讓助理回答關於本人 Fitatu 日記的問題,例如今天攝取多少熱量或本週進度,或在不開啟應用程式的情況下記錄餐點、飲水、體重與運動。最適合個人單一帳號在桌面端 MCP 用戶端使用。
- 執行需求
- 以 stdio 在本機執行,通常透過 uvx 從帶 mcp 附加項的 fitatu-api 套件啟動;需要 Python 3.11 或更新版本。需要 Fitatu 帳號,以及由 fitatu-api login 指令(Apple/Facebook 帳號用 import-session)產生的工作階段檔案,預設位於使用者家目錄,也可用 FITATU_SESSION 變數指定。需要連線至 Fitatu 各區域 API 主機的網路。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Fitatu,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
[fitatu-api: your Fitatu food diary for AI agents (unofficial)]
fitatu-api
[PyPI] [Python] [Status] [License] [CI] [Fitatu MCP on mcprush]
An unofficial Python client for the API of Fitatu, the calorie
counter, built to be driven by AI agents and scripts. It reads and writes the food diary,
activities, body measurements and water, reads and sets goals, and reads summaries and the food
catalog.
FitatuTools turns the diary into tools a language model can call. The session handling
follows python-garminconnect: log in once,
keep one session file, let the client refresh it.
Unofficial. This project is not affiliated with or endorsed by Fitatu Sp. z o.o. The API was reverse-engineered from the Fitatu apps and can change without notice. Automated access may break Fitatu's Terms of Service; check them, and use the library only with your own account and at your own risk. The MIT licence covers this code, not the Fitatu service, its API or its data.
Install
Python 3.11 or newer; the only dependency is requests (and tzdata on Windows).
To work on the library or run its tests offline, clone it instead: git clone https://github.com/Jezue/fitatu_library && cd fitatu_library && pip install -e ".[dev]".
Quick start
Signed up with Apple or Facebook? Then the account has no password: copy the session from the
web app with fitatu-api import-session instead, as described under
Authentication & session file.
How writes, row ids and removals work: docs/PLANNER.md.
To copy a catering diet's menu into the diary (safe to run again), use catering_sync.py from
the repository: Importing a catering menu.
For AI agents
FitatuTools exposes 47 tools (read the day, search food, log food, weight, water,
workouts, private recipes and products, ...) with JSON Schema inputs and read-only / destructive / idempotent annotations.
call() never raises: it returns {"ok": true, "data": ...} or {"ok": false, "error": {"error_type", "message", "retryable", "hint", ...}}, so the model can read what went wrong.
SYSTEM_PROMPT is the recommended system prompt: the rules the model needs on top of the tool
descriptions (read before writing, users' own meal names, data.ok, confirmations). The model
asks get_today for the date, in the account's time zone.
The write tools (log food, update, move, copy and delete rows, weight, water, workouts, private
recipes and products) need a loop
that asks the user before destructive calls; it is in
docs/AGENTS.md, with the
tool table, the calls never to expose (session tokens, catalog writes), how to make writes safe
to repeat, what to do per error_type, the OpenAI variant, long-running hosts, and how to try
the tools without an account against the offline test backend.
MCP
fitatu-mcp serves the same tools to Claude Code, Claude Desktop and other MCP hosts over
stdio, with SYSTEM_PROMPT as its instructions and three Polish slash commands
(podsumowanie_dnia, przeglad_tygodnia, zaloguj_posilek):
fitatu-mcp --http serves the same tools over HTTP, with a token, for n8n, another computer or
Docker (the repository has a Dockerfile):
docs/AGENTS.md.
Without an account, fitatu-mcp --demo (or FitatuTools.demo()) serves a small made-up diary
kept in memory. For Claude Desktop, each GitHub release
has fitatu-<version>.mcpb, installed with a double click (log in with fitatu-api login first). Without installing anything, uvx --from "fitatu-api[mcp]" fitatu-mcp runs the server. It is
listed in the MCP Registry as
io.github.Jezue/fitatu-api (server.json). For Hermes Agent and other hosts that read
Agent Skills, skills/fitatu-diary/SKILL.md
tells the agent how to set the server up and use its tools well.
The host's permission prompt is the confirmation step, so keep it on for the destructive tools. Claude Desktop, uvx and what the server does: docs/AGENTS.md.
What it covers
FitatuLibrary, which wraps most of these as *_via_api methods that return {"status": ...}
instead of raising, is deprecated and will be removed in 1.0: for results that never raise use
FitatuTools ({"ok": true, "data": ...}), otherwise the client. Every public call with its stability is listed in
docs/API_REFERENCE.md.
Free and Premium accounts
Nearly everything works on an account without Fitatu Premium. Fitatu answers 403 without it to (checked live 2026-10-15):
summary.get_nutrient_by_day()for protein, fat and carbohydrate (energy works on any account);summary.get_top_products(), and so theget_top_foodstool;summary.export_diary(), and sofitatu-api export, which then says so in one line.
Fitatu serves the recipe search filters and premium catalog recipes to any account, but its app
keeps them for Premium, and so do the tools: they check the account's accessControl flags and
answer a forbidden error without them. The client itself does not check.
get_progress and get_report work on every account: when the macros' day-by-day route is
refused, they read each day's own summary instead (a request per day, at most 31 days), and
get_report gives top_foods null with a top_foods_note. A free account's macro goals are a
range (target_min below target_max); on a Premium account a one-day summary gave the two
equal (the day-by-day route get_progress reads there agrees, checked live). It takes a manual
goal and its own macro split too (set_targets, checked live 2026-10-15); with a split, its
macro goals are single numbers. fitatu-api doctor
says whether the account has a premium plan. See
Free and Premium accounts
in docs/AGENTS.md for the tools.
Command line
fitatu-api (also python -m fitatu_api) works on the session file, $FITATU_SESSION or
~/.fitatu/session.json:
status prints where the session comes from (the file and its permissions, or
$FITATU_SESSION_JSON), the user id, API host, locales, when the bearer token expires and
whether there is a refresh token, without sending anything; --check also reads the profile
once. doctor checks the setup with Fitatu, one ok / WARN / FAIL line each: the session
file's permissions, the refresh token (it refreshes the bearer once and saves it, as any
refresh does), this computer's clock against the fresh token's issue time, a profile read,
whether a premium plan runs (not until when, nor its price), and whether Fitatu's web app is still at the version the library sends (GET /upgrade-application/WEB/<version>); it exits with 1 when a check failed and prints no
token, user id or home directory, so its output can go into a bug report. logout deletes the
session file; stop fitatu-mcp and other processes that use it first, as they would write
their refreshed tokens back. Fitatu has no call the library knows of that revokes tokens.
None of these commands prints a token. fitatu-api --version prints the version.
call runs one tool through FitatuTools.call() and prints its envelope as JSON; the exit
status is 0 when ok is true and 1 when it is false. Only the read tools (READ_ONLY_TOOLS)
run unless --allow-writes is given. export saves Fitatu's diary export (csv, xls, pdf
or txt) to a new file readable only by you (0600); it does not replace an existing file
without --overwrite, and a range with nothing logged writes no file and exits with 1.
fitatu-api COMMAND --help describes each command. Logs go to stderr, from WARNING unless
fitatu-api --log-level DEBUG COMMAND or FITATU_LOG_LEVEL says otherwise (see
Privacy & logging).
Authentication & session file
fitatu-api login [--email ADDRESS] [-o PATH] [--base-url URL] (also python -m fitatu_api login) logs in, reads your profile for the account's locales and timezone, and writes the
session file. It reads FITATU_EMAIL and
FITATU_PASSWORD when they are set and prompts otherwise. Accounts without a password use
fitatu-api import-session (below).
-
Where.
fitatu-api login,fitatu-api import-session,FitatuApiClient.from_session(),FitatuTools.from_session()and the deprecatedFitatuLibrary.from_session()use the same file: the path you pass (-ofor the CLI), else$FITATU_SESSION, else$FITATU_SESSION_PATH, else~/.fitatu/session.json. A missing file raisesFitatuAuthErrortelling you to runfitatu-api login. -
What. JSON with
bearer_token,refresh_token,fitatu_user_id,base_url(the region's API host),app_locale,app_searchlocale,app_storagelocale,app_timezoneand a schema version. The client's identity (API key, app version, ...) is not stored; it comes from the library. -
Permissions. The file is written owner-only (0600), in a directory created 0700, through a temporary file that replaces it, so a reader never sees half a session. It holds live tokens: treat it like a password.
-
Tokens. Checked live on 2026-10-13: the bearer token is a JWT that lives one hour. The refresh token is an opaque string that Fitatu does not rotate:
POST /token/refreshreturns a new bearer token and the same refresh token, which keeps working, and older bearer tokens stay valid until they expire.POST /loginanswers a wrong e-mail or password with 422, not 401. -
Refresh and write-back. The client refreshes the bearer token 60 seconds before it expires (from the JWT
exp) and once after a 401, one refresh at a time. The tokens are merged into the session file, so the next process starts with them; a process that shares the file picks up a pair another one saved, e.g. afterfitatu-api loginran again. Passpersist_tokens=Falseto keep the file untouched. A pair of another account (a login to another account with the same file) is not picked up: the running client keeps its own tokens and stops writing to the file, so that login stays. When a token fresh from Fitatu already looks expired, the computer's clock runs fast: the client then logs one warning and refreshes only after a 401. -
Logging in again. When Fitatu rejects the refresh token, calls raise
FitatuAuthError(error_typeauth). Runfitatu-api login(orimport-session) again; a running client picks the new pair up from the file the next time it needs a refresh. -
Accounts without a password. Accounts created with Apple or Facebook (Apple's "Hide my e-mail" addresses included) have no password for
fitatu-api login. Copy the session from the web app instead:-
Log in at https://www.fitatu.com/app/ with Apple or Facebook.
-
Open the browser's developer console on that page and run the line below. It copies only the tokens and your user id; the web app's
localStorage.useralso holds your e-mail, nickname and more. -
On macOS run
pbpaste | fitatu-api import-session. On Linux or Windows paste it into a file, runfitatu-api import-session < file(PowerShell:Get-Content file | fitatu-api import-session) and delete the file.
import-session [-o PATH] [--base-url URL]reads the JSON from stdin (a session file of this library works too), checks both tokens with Fitatu by refreshing the bearer once (so the saved one is good for an hour), reads the account's locales and timezone likeloginand writes the session file the same way. Nothing else from the input is saved. The token is a full account credential: do not paste it anywhere else. -
-
Containers and cron. Where no writable session file fits, put the session JSON itself in
FITATU_SESSION_JSON:fitatu-api login --print(orimport-session --print) prints it to stdout instead of writing a file, for a secret store.from_session()without a path takes it before any file, and keeps the tokens it refreshes in memory: Fitatu does not rotate the refresh token, so the same value works for every run until the refresh token itself expires (how long it lives is not known yet) or you log in again. -
Facade (deprecated).
FitatuLibrary.from_session()loads the same file and writes refreshed tokens back to it.FitatuLibrary(session_data, session_path=...)does the same for a session dict, and copies the current pair intosession_dataafter every call. -
From Python.
FitatuApiClient.login(email, password, token_store_path=path)logs in and merges the whole session intopath.
Regions & headers
The API host is the session's base_url, else the host of the account's storage locale:
pl_PL becomes https://pl-pl.fitatu.com/api, de_DE becomes https://de-de.fitatu.com/api.
fitatu-api login logs in on https://pl-pl.fitatu.com/api and stores the storage locale's
host; with --base-url it logs in there and stores that URL.
If Fitatu changes what the web app sends (its version, or the API key and secret) before a
release of the library follows, set FITATU_CLIENT_PROFILE to a JSON object with the keys of
WEB_CLIENT_PROFILE to change, e.g. FITATU_CLIENT_PROFILE='{"app_version": "4.14.0"}'. Any
other key or a value that is not plain text is refused with a FitatuValidationError
(fitatu-mcp then does not start). fitatu-api doctor says whether the version is still the
newest.
Login (POST /login with _username and _password) and refresh (POST /token/refresh with
{"refresh_token": ...}) go through the same request path and send the same headers, without
Authorization; at login the locale headers are still the pl_PL defaults. There is no
request signing, and Firebase App Check is not enforced; the library does not send it.
Credentials only go to https hosts under fitatu.com: any other base_url or absolute URL
raises FitatuValidationError before a request is made, and Authorization is only sent to
URLs under base_url or https://fitatu.com/api. Redirects are not followed: a 3xx answer is
a FitatuApiError, so a request body never goes on to another host.
Errors, retries, rate limits
The library's errors are FitatuApiError subclasses. describe_error(exc) turns any
exception into the JSON-safe dict that FitatuTools.call() (and the deprecated FitatuLibrary) return.
Messages read METHOD /path -> status CODENAME: server message. In describe_error()'s message
the user id is <user> (GET /diet-plan/<user>/days -> 403), and with user_id= (which
FitatuTools.call() passes) wherever else it appears, so a result does not carry it to the
model provider. A 403 whose body is an HTML page comes from a firewall in front of the API (e.g.
Cloudflare), not from Fitatu's premium or key checks, and its hint says so. Any other exception, such as a TypeError raised inside the
library, is unexpected: a bug to report, since FitatuTools.call() checks the arguments first.
Retries: network errors, 429, 500/502/503/504 and Cloudflare's 520 to 524 are retried for GET, HEAD and OPTIONS,
and for writes marked idempotent: the day sync (all planner writes; it upserts by row id),
water.set_day, measurements.set_day, measurements.delete_day and foods.set_favorite.
Other writes (catalog writes, settings, login, refresh) are sent once. Up to
retry_max_attempts=3 attempts with exponential backoff from retry_base_delay_seconds=0.5.
A Retry-After of up to 30 seconds is honoured; a longer one is not waited for, and the error
is raised at once (FitatuRateLimitError with retry_after for a 429). During a technical
break nothing is retried or refreshed. Inside with client.time_limit(seconds): the requests
end within that time in all: a retry whose wait would leave it less than 2 seconds is not made, each request's
timeout is cut to the time left, and a request with no time left is not sent
(FitatuConnectionError). FitatuTools runs every call in one of 45 seconds (call_timeout).
Most writes make several requests, which counts when you budget against 429s: the tools read
the day before every diary write, and updates, moves, removals and activity writes read it
again afterwards to set ok. Requests per agent tool call, before retries and token refreshes:
Privacy & logging
The library logs through the standard logging module and configures no handlers. The
fitatu-api command and fitatu-mcp log to stderr, never stdout (which carries the command's
result, or the MCP protocol): the library from WARNING, or from the level that --log-level DEBUG|INFO|WARNING|ERROR (for fitatu-api before the command, e.g. fitatu-api --log-level DEBUG call get_today) or FITATU_LOG_LEVEL sets; other libraries from WARNING.
INFO(fitatu_api.client): auth events as JSON lines: login (user id), token refresh start, success and failure, andclear_auth, with a non-secret state snapshot (user id, locales, timezone, host, token expiry, whether tokens are present).WARNING(fitatu_api.client):fitatu.request.errorfor a request that failed for good on a network error, a 429 or a 5xx, after its retries;fitatu.auth.clock_skew(a token fresh from Fitatu looks expired, so the local clock is off; refreshing ahead of expiry stops) andfitatu.auth.store_other_account(the session file now holds another account's tokens; the client keeps its own). Fromfitatu_api.agent,fitatu_api.facadeandfitatu_api.mcp_server, a call whose result reports anunexpectederror (with the traceback), anauthor amaintenanceerror:get_day failed (auth): .... Fromfitatu_api.auth, a token store (usually the session file) that could not be read. Fromfitatu_api.mcp_serverunder--http, each request refused for its token, with the client's address (never the token); uvicorn adds its start and stop lines.DEBUG(fitatu_api.client): every request as JSON lines:fitatu.request.start(method, path, query parameters such as search phrases),fitatu.request.retrybefore each retry's wait (the failedattempt,delay_seconds, and thereason: the status or the network error's type), thenfitatu.request.okorfitatu.request.error(status,attempt,elapsed_mssince the start, and the first 300 characters of an error body; a 4xx stays at DEBUG, since the caller gets it as an exception and some, such as a 404, are expected). Fromfitatu_api.planner, one line per planner write (user id, day, meal, food id, a custom row's name and kcal); from the tool layers, every other error a result reports. Paths contain the user id and dates.
Request bodies, passwords and tokens are never logged: values under keys containing token,
secret, password or authorization are replaced with [redacted], and the auth context
keeps tokens and the API secret out of its repr. Error messages carry up to 200 characters of
the server's message; the full response body is in exc.body, which describe_error() and
the error results of FitatuTools (and the deprecated FitatuLibrary) leave out. A token that is not a valid header value (e.g. pasted with a
newline) is reported without quoting it.
Testing
The suite runs offline. tests/fixtures/api_recording.json holds real API responses (scrubbed
of personal data) for the day sync, measurements, summaries, search and settings;
tests/fake_server.py is a fake backend built from them, and tests/test_recorded_contract.py
fails if the fake drifts from the recording. How the recording is made and refreshed:
tests/fixtures/README.md.
The live suite (tests/test_live_all.py) runs against a real account and writes to it:
It uses the session file that fitatu-api login wrote (or $FITATU_SESSION) and saves
refreshed tokens back to it; with $FITATU_SESSION_JSON set, that session is used instead and
refreshed tokens are not saved. Optional: FITATU_LIVE_DAY (after today; default a year from
today), FITATU_LIVE_MEAL_KEY (snack), FITATU_LIVE_PRODUCT_ID, FITATU_LIVE_RECIPE_ID,
FITATU_LIVE_MEASUREMENT_DAY (2020-01-01; skipped if it has a measurement) and
FITATU_LIVE_RUN_WRITE_ACCOUNT=1.
Each test deletes exactly the rows it added on FITATU_LIVE_DAY and fails if one is left; a
day on or before today is refused. Water and the note on that day are set back, and the weight
and measurements written on FITATU_LIVE_MEASUREMENT_DAY are deleted. -k ReadOnly runs only
the tests that read.
Logging catalog foods adds them to the account's recent foods, which is not undone. With
FITATU_LIVE_RUN_WRITE_ACCOUNT=1 the suite also changes and restores the water glass size and the
daily goals (Fitatu keeps the restored goals as a new version from today), and creates, then
deletes, a private product and two private recipes (one through the tools, logged on
FITATU_LIVE_DAY and removed) and a private product through the tools (logged on
FITATU_LIVE_DAY, changed, removed). Product and recipe deletes are soft, so like the recent
foods they are not fully undone: they stay on the account, marked deleted and hidden from your
foods and searches. The full list is in the module docstring of tests/test_live_all.py. Never point FITATU_LIVE_DAY at a day with entries you care about.
scripts/check_contract.py tells, reading only, whether the live API still answers with the
structure the recording holds: it prints route and key names, never values, and exits 1 when a
key is gone, a type changed or a route fails. The Live contract workflow runs it and the
read-only live tests every Monday with a throwaway account's session in the FITATU_SESSION_JSON
secret, and skips without it:
CONTRIBUTING.md.
scripts/eval_agent.py scores how well a Claude model uses the tools on the demo diary (29
tasks in Polish and English; needs ANTHROPIC_API_KEY and costs API usage, so CI does not run
it): docs/AGENTS.md.
Documentation
- docs/AGENTS.md: tools, safe writes, error handling and the MCP server for agents.
- docs/PLANNER.md: the day sync contract, ids, and the result of every diary write.
- docs/API_REFERENCE.md: every public class and method with its stability.
- docs/APP_4_17_1_API_CATALOG.md: every route of the Fitatu Android app 4.17.1, from a decompile.
- docs/RELEASING.md: release steps for maintainers.
- CHANGELOG.md
Bugs and questions: GitHub issues. Security problems are reported privately: SECURITY.md. Setup, checks and rules for pull requests: CONTRIBUTING.md.
License
MIT, see LICENSE. It covers this code only, not the Fitatu service, its API or the data it returns.
來源:README.md,提交 7dda889
工具
0版本歷史
3- v0.6.0最新Oct 15, 2026
- v0.5.1Oct 15, 2026
- v0.5.0Oct 14, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分