关于 Fitatu
Fitatu 是用于读取和更新 Fitatu 账户饮食日记及相关健康数据的非官方 Python 客户端和 MCP 服务,面向脚本与 AI Agent,提供结构化工具和命令行操作。
主要功能
- 01读取和编辑饮食日记
- 02记录活动、身体测量和饮水
- 03搜索食物、食谱和活动
- 04读取摘要、目标和习惯
- 05管理会话、工具及导出的命令行
适用场景
- 01通过 AI Agent 记录餐食
- 02查看体重、饮水和运动记录
- 03汇总指定日期范围的日记数据
MCP 相关
`fitatu-mcp` 将 Agent 工具通过标准输入输出或 HTTP 提供给 MCP 主机,也支持只读和演示模式。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
47收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
fitatu-api
这是一个非官方的 Python 客户端,用于访问卡路里计数应用 Fitatu 的 API,专为 AI Agent 和脚本设计。它可以读取和写入饮食日记、活动、身体测量数据和饮水记录,读取和设置目标,并读取摘要和食物目录。
FitatuTools 会将饮食日记转换为大模型可调用的工具。会话处理方式参照 python-garminconnect:登录一次,保留一个会话文件,并由客户端刷新会话。
非官方。 本项目与 Fitatu Sp. z o.o. 无关,也未获得其认可。此 API 是通过逆向分析 Fitatu 应用而得,可能随时变更,恕不另行通知。自动访问可能违反 Fitatu 的服务条款;请先查阅相关条款,并仅使用自己的账户,风险自负。MIT 许可证仅适用于此代码,不适用于 Fitatu 服务、其 API 或数据。
安装
需要 Python 3.11 或更高版本;唯一的依赖项是 requests(Windows 上还需要 tzdata)。
pip install fitatu-api # the library and the fitatu-api command
pip install "fitatu-api[mcp]" # plus fitatu-mcp, the MCP server
pip install "git+https://github.com/Jezue/fitatu_library" # unreleased changes from main
若要开发此库或离线运行测试,请改为克隆仓库:git clone https://github.com/Jezue/fitatu_library && cd fitatu_library && pip install -e ".[dev]"。
快速开始
fitatu-api login # asks for e-mail and password, writes ~/.fitatu/session.json
使用 Apple 或 Facebook 注册?这类账户没有密码:请按身份验证与会话文件中的说明,通过 fitatu-api import-session 从 Web 应用复制会话。
from datetime import date
from fitatu_api import FitatuApiClient
with FitatuApiClient.from_session() as client: # closes the HTTP connections at the end
user_id = client.auth.fitatu_user_id
today = date.today()
day = client.planner.get_day(user_id, today)
for meal_key, meal in day["dietPlan"].items():
for row in meal["items"]:
print(meal_key, row["name"], row["energy"], row["planDayDietItemId"])
egg = client.search_food("jajko kurze", limit=1)[0] # search rows carry the id as foodId
added = client.planner.add_product_to_day_meal(
user_id, today, meal_type="breakfast", product_id=egg["foodId"],
measure_id=egg["measure"]["measureId"], measure_quantity=2, # 2 of the hit's measure
)
print("added row", added["row_id"])
了解写入、行 ID 和移除的工作方式:docs/PLANNER.md。
若要将餐食配送套餐的菜单复制到饮食日记中(可安全重复运行),请使用仓库中的 catering_sync.py:导入餐食配送菜单。
面向 AI Agent
FitatuTools 提供 47 个工具(读取当天数据、搜索食物、记录食物、体重、饮水、锻炼、私人食谱和产品等),具有 JSON Schema 输入以及只读/破坏性/幂等注释。
call() 不会抛出异常:它会返回 {"ok": true, "data": ...} 或 {"ok": false, "error": {"error_type", "message", "retryable", "hint", ...}},因此模型可以了解出错原因。
SYSTEM_PROMPT 是推荐使用的系统提示词,其中包含模型需要遵循的规则,用于补充工具说明(写入前先读取、使用用户自己的餐食名称、检查 data.ok、确认操作)。模型会调用 get_today 获取账户时区中的日期。
import json
import anthropic
from fitatu_api import FitatuTools
from fitatu_api.agent import READ_ONLY_TOOLS, SYSTEM_PROMPT, anthropic_tools # openai_tools() for OpenAI
client = anthropic.Anthropic()
messages = [{"role": "user", "content": "How many kcal did I eat today?"}]
# Only the read tools, as this loop runs tool calls without asking the user: call() refuses
# any other tool, which the model can still name.
with FitatuTools.from_session(tools=READ_ONLY_TOOLS) as tools:
while True:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
system=SYSTEM_PROMPT,
tools=anthropic_tools(READ_ONLY_TOOLS),
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
break
results = [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(tools.call(block.name, block.input)),
}
for block in response.content
if block.type == "tool_use"
]
messages.append({"role": "user", "content": results})
if response.stop_reason != "end_turn": # max_tokens or refusal: the answer is cut or missing
raise RuntimeError(f"the model stopped early: {response.stop_reason}")
print("".join(block.text for block in response.content if block.type == "text"))
写入工具(记录食物、更新、移动、复制和删除行、体重、饮水、锻炼、私人食谱和产品)需要配合一个在执行破坏性调用前询问用户的循环;相关说明见
docs/AGENTS.md,其中包含工具表、不得公开的调用(会话令牌、目录写入)、如何确保写入可安全重复、针对各 error_type 的处理方法、OpenAI 变体、长时间运行的主机,以及如何在没有账户的情况下使用离线测试后端试用这些工具。
MCP
fitatu-mcp 通过 stdio 向 Claude Code、Claude Desktop 和其他 MCP 主机提供相同的工具,并将 SYSTEM_PROMPT 作为指令,还提供三个波兰语斜杠命令(podsumowanie_dnia、przeglad_tygodnia、zaloguj_posilek):
pipx install "fitatu-api[mcp]"
fitatu-api login
claude mcp add fitatu --scope user -- fitatu-mcp # fitatu-mcp --read-only: the read tools only
fitatu-mcp --http 通过 HTTP 提供相同的工具,并使用令牌,适用于 n8n、另一台计算机或 Docker(仓库中包含 Dockerfile):
docs/AGENTS.md。
无需账户,fitatu-mcp --demo(或 FitatuTools.demo())即可提供一份保存在内存中的小型虚构日记。对于 Claude Desktop,每个 GitHub release
都包含 fitatu-<version>.mcpb,双击即可安装(请先使用 fitatu-api login 登录)。无需安装任何内容,运行 uvx --from "fitatu-api[mcp]" fitatu-mcp 即可启动服务。它已在 MCP Registry 中登记,名称为
io.github.Jezue/fitatu-api(server.json)。对于 Hermes Agent 和其他读取
Agent Skills 的主机,skills/fitatu-diary/SKILL.md
会指导 Agent 配置服务并妥善使用其工具。
主机的权限提示就是确认步骤,因此请为具有破坏性的工具保留该提示。关于 Claude Desktop、uvx 以及服务的功能: docs/AGENTS.md。
涵盖范围
| 领域 | 客户端(FitatuApiClient) | Agent 工具 |
|---|---|---|
| 食物日记 | client.planner:读取某一天、添加产品、食谱、自定义行和搜索结果、更新、移动、复制、删除;当天的备注 | get_day、add_food、add_foods、add_custom_item、update_item、move_item、copy_meal、preview_removal、remove_items、set_note |
| 活动 | planner.add_activity / remove_activity、client.activities(目录、搜索、最近使用) | search_activity、add_activity、remove_activity |
| 食物 | search_food、get_product_details、get_recipe、食谱目录及其搜索、client.foods(收藏、最近使用) | search_food、search_recipes、get_recipe、find_by_barcode、get_food_measures、get_recent_foods、get_favorite_foods、set_favorite |
| 用户自己的食物 | search_user_food、create_product、update_product、delete_product、create_recipe、update_recipe、delete_recipe | get_my_foods;create_food、update_food、delete_food,以及仅用于用户自己的私有食物的食谱工具 |
| 测量数据 | client.measurements:每天的体重和身体尺寸、历史记录、图表 | get_weight_history、get_body_measurements、get_measurement_history、log_weight、log_measurements |
| 饮水 | client.water | get_water、set_water |
| 摘要和导出 | client.summary:时间范围汇总、每日营养素、排名靠前的产品、日记导出 | get_range_summary、get_progress、get_top_foods、get_report、get_shopping_list(根据计划行计算) |
| 断食和习惯 | client.fasting(方案、每天的进食时段)、client.habits(目录、用户的习惯、状态良好的日子);仅供读取 | get_fasting、get_habits、get_good_days |
| 目标和设置 | client.user_settings:目标、BMR/TMR、个人资料 | get_targets、set_targets(手动或自动目标、宏量营养素比例、杯子容量)、get_profile(身高、体重、目标、BMI、Premium;不包含任何可识别用户身份的信息) |
| 目录写入 | create_recipe(shared=True)、set_product_proposal、重复项清理 | 无,特意如此 |
FitatuLibrary 将其中大部分功能封装为 *_via_api 方法,这些方法返回 {"status": ...},而不是抛出异常。它已弃用,并将在 1.0 中移除:若要使用不会抛出异常的方法处理结果,请使用
FitatuTools({"ok": true, "data": ...});否则请使用客户端。所有公开调用及其稳定性均列于
docs/API_REFERENCE.md。
免费和 Premium 账户
| 免费账户 | Premium | |
|---|---|---|
| 日记:读取、记录、更新、移动、复制、移除食物、笔记、活动、饮水、体重、身体测量数据 | ✅ | ✅ |
搜索、条形码、食物份量、按关键词和 max_energy_kcal 搜索食谱、完整食谱、私人食谱和产品、收藏、最近食物 | ✅ | ✅ |
食谱搜索筛选条件(min_protein_g、max_carbohydrate_g、max_cooking_time、diets 和 meal)和 Premium 目录食谱(get_recipe、get_food_measures、add_food、add_foods;购物清单不包含这些食谱的食材) | ❌“需要 Fitatu Premium”:工具遵循应用的规则 | ✅ |
目标:读取和设置(get_targets、set_targets)、进度(get_progress)、报告(get_report)、日期范围汇总(get_range_summary) | ✅(进度:每天一个请求,最多 31 天) | ✅ |
| 体重和身体测量历史记录、个人资料、禁食和习惯(读取)、购物清单 | ✅ | ✅ |
常吃食物(get_top_foods、summary.get_top_products()) | ❌ 403:需要 Fitatu Premium | ✅ |
日记导出(fitatu-api export、summary.export_diary()) | ❌ 403:“需要 Fitatu Premium” | ✅ |
每日蛋白质/脂肪/碳水化合物(summary.get_nutrient_by_day();能量在两种账户上都可用) | ❌ 403(get_progress 会绕开此限制读取数据) | ✅ |
几乎所有功能都可以在没有 Fitatu Premium 的账户上使用。Fitatu 在无 Premium 的情况下会对以下功能返回 403(于 2026-10-15 实时检查):
- 蛋白质、脂肪和碳水化合物的
summary.get_nutrient_by_day()(任何账户都可读取能量); summary.get_top_products(),因此get_top_foods工具也不可用;summary.export_diary(),因此fitatu-api export也不可用,随后会用一行文字说明原因。
Fitatu 会向任何账户提供食谱搜索筛选条件和 Premium 目录食谱,但其应用仅对 Premium 用户开放这些功能,因此工具也遵循这一限制:它们会检查账户的 accessControl 标志,没有相应标志时会返回 forbidden 错误。客户端本身不会进行检查。
get_progress 和 get_report 对所有账户都可用:当每日宏量营养素路由被拒绝时,它们会改为读取每天各自的汇总(每天一个请求,最多 31 天);get_report 会将 top_foods 设为 null,并提供 top_foods_note。免费账户的宏量营养素目标是一个范围(target_min 小于 target_max);在 Premium 账户上,某一天的汇总显示这两个值相等(get_progress 读取的每日路由结果也一致,已实时检查)。它也接受手动目标和单独设置的宏量营养素比例(set_targets,于 2026-10-15 实时检查);设置比例后,宏量营养素目标会变成单个数值。fitatu-api doctor
会说明账户是否拥有 Premium 方案。有关这些工具,请参阅 docs/AGENTS.md 中的免费和 Premium 账户。
命令行
fitatu-api(也可以通过 python -m fitatu_api 运行)使用会话文件 $FITATU_SESSION 或
~/.fitatu/session.json:
fitatu-api login # or import-session, see Authentication & session file below
fitatu-api status # the session: file, user id, host, token expiry; sends nothing
fitatu-api doctor # checks the session, the clock and the client version with Fitatu
fitatu-api logout # deletes the session file after asking (--yes: without asking)
fitatu-api tools # the agent tools: name, read / write / destructive, what it does
fitatu-api tools --json # the same tools in the Anthropic API format, anthropic_tools()
fitatu-api call get_day '{"date": "2026-10-14"}'
fitatu-api export --from 2026-10-01 --to 2026-10-14 --format csv -o diary.csv # needs Fitatu Premium
status 会显示会话的来源(文件及其权限,或 $FITATU_SESSION_JSON)、用户 ID、API 主机、区域设置、Bearer 令牌的过期时间,以及是否存在刷新令牌,不会发送任何内容;--check 还会读取一次个人资料。doctor 会通过 Fitatu 检查配置,每项检查各输出一行 ok / WARN / FAIL:会话文件的权限、刷新令牌(会刷新一次 Bearer 令牌并保存,和任何刷新操作一样)、这台计算机的时钟与新令牌签发时间是否一致、个人资料读取、Premium 套餐是否正在运行(不显示到期时间或价格),以及 Fitatu 的 Web 应用是否仍处于该库发送请求时使用的版本(GET /upgrade-application/WEB/<version>);如果有检查失败,则退出码为 1。它不会输出令牌、用户 ID 或主目录,因此其输出可以放入错误报告。logout 会删除会话文件;请先停止 fitatu-mcp 和其他使用该文件的进程,否则这些进程会将刷新后的令牌写回文件。Fitatu 没有此库已知的可用于撤销令牌的调用。这些命令都不会输出令牌。fitatu-api --version 会显示版本。
call 会通过 FitatuTools.call() 运行一个工具,并将其信封以 JSON 格式输出;当 ok 为 true 时,退出状态为 0,否则为 1。除非指定 --allow-writes,否则只会运行读取工具(READ_ONLY_TOOLS)。export 会将 Fitatu 的日记导出内容(csv、xls、pdf 或 txt)保存到一个仅你可读取(0600)的新文件;除非指定 --overwrite,否则不会覆盖现有文件。如果指定范围内没有记录,则不会写入文件,且退出码为 1。fitatu-api COMMAND --help 会说明每个命令。日志会写入 stderr,默认级别为 WARNING,除非 fitatu-api --log-level DEBUG COMMAND 或 FITATU_LOG_LEVEL 指定了其他级别(参见隐私与日志记录)。
身份验证与会话文件
fitatu-api login [--email ADDRESS] [-o PATH] [--base-url URL](也可以通过 python -m fitatu_api login 运行)会登录、读取你的个人资料以获取账户的区域设置和时区,并写入会话文件。如果已设置 FITATU_EMAIL 和
FITATU_PASSWORD,则会读取这两个变量;否则会提示你输入。没有密码的账户请使用 fitatu-api import-session(见下文)。
-
位置。
fitatu-api login、fitatu-api import-session、FitatuApiClient.from_session()、FitatuTools.from_session()和已弃用的FitatuLibrary.from_session()使用同一个文件:你传入的路径(CLI 使用-o),否则是$FITATU_SESSION,否则是$FITATU_SESSION_PATH,否则是~/.fitatu/session.json。如果文件不存在,会抛出FitatuAuthError,提示你运行fitatu-api login。 -
内容。 JSON,包含
bearer_token、refresh_token、fitatu_user_id、base_url(该地区的 API 主机)、app_locale、app_searchlocale、app_storagelocale、app_timezone以及架构版本。客户端的身份信息(API 密钥、应用版本等)不会存储在其中,而是来自库。 -
权限。 文件仅允许所有者读写(0600),并存储在权限为 0700 的目录中;写入时会先使用临时文件再替换原文件,因此读取者不会看到不完整的会话文件。文件中包含有效令牌:请像对待密码一样保护它。
-
令牌。 于 2026-10-13 实时检查:bearer 令牌是有效期为一小时的 JWT。refresh 令牌是 Fitatu 不会轮换的不透明字符串:
POST /token/refresh会返回新的 bearer 令牌和相同的 refresh 令牌;后者会持续有效,而旧的 bearer 令牌会在过期前保持有效。对于错误的电子邮件或密码,POST /login会返回 422,而不是 401。 -
刷新与回写。 客户端会在 bearer 令牌过期前 60 秒(根据 JWT
exp)刷新令牌,并在收到 401 后再刷新一次;同一时间只会进行一次刷新。令牌会合并写入会话文件,因此下一个进程启动时会使用这些令牌;共享该文件的进程也会读取其他进程保存的令牌对,例如在再次运行fitatu-api login后。传入persist_tokens=False可保持文件不变。其他账户的令牌对(使用同一个文件登录另一个账户时)不会被读取:正在运行的客户端会继续使用自己的令牌,并停止向文件写入,因此该次登录会保留。如果 Fitatu 刚返回的令牌看起来已经过期,则说明电脑时钟走快了:此时客户端只会记录一条警告,并在收到 401 后才刷新。 -
重新登录。 如果 Fitatu 拒绝 refresh 令牌,调用会抛出
FitatuAuthError(error_type为auth)。请再次运行fitatu-api login(或import-session);正在运行的客户端会在下次需要刷新时从文件中读取新的令牌对。 -
没有密码的账户。 使用 Apple 或 Facebook 创建的账户(包括使用 Apple“隐藏我的电子邮件”地址的账户)没有可用于
fitatu-api login的密码。请改为从 Web 应用复制会话:-
使用 Apple 或 Facebook 登录 https://www.fitatu.com/app/。
-
在该页面打开浏览器的开发者控制台,并运行下面这行代码。它只会复制令牌和你的用户 ID;Web 应用的
localStorage.user还包含你的电子邮件、昵称等信息。copy(JSON.stringify((u => ({token: u.token, refresh_token: u.refresh_token, id: u.id}))(JSON.parse(localStorage.user)))) -
在 macOS 上运行
pbpaste | fitatu-api import-session。在 Linux 或 Windows 上,将其粘贴到文件中,运行fitatu-api import-session < file(PowerShell:Get-Content file | fitatu-api import-session),然后删除该文件。
import-session [-o PATH] [--base-url URL]会从 stdin 读取 JSON(本库的会话文件也可以),通过刷新一次 bearer 令牌向 Fitatu 验证两个令牌(因此保存的 bearer 令牌可使用一小时),读取账户的区域设置和时区(与login相同),并以相同方式写入会话文件。 输入中的其他内容都不会保存。令牌是完整的账户凭据:不要在其他任何地方粘贴它。 -
-
容器和 cron。 如果无法使用可写的会话文件,可将会话 JSON 本身放入
FITATU_SESSION_JSON:fitatu-api login --print(或import-session --print)会将其打印到标准输出,而不写入文件,便于存入机密存储。from_session()未指定路径时,会优先使用它,而不是任何文件;刷新后的令牌会保存在内存中:Fitatu 不会轮换刷新令牌,因此每次运行都可以使用同一个值,直到刷新令牌本身过期(目前尚不清楚其有效期)或你再次登录。 -
门面(已弃用)。
FitatuLibrary.from_session()会加载同一个文件,并将刷新的令牌写回该文件。FitatuLibrary(session_data, session_path=...)对会话字典也执行相同操作,并在每次调用后将当前令牌对复制到session_data中。 -
从 Python 使用。
FitatuApiClient.login(email, password, token_store_path=path)会登录并将整个会话合并到path中。
地区和请求头
API 主机取自会话中的 base_url;若未设置,则使用账户存储地区对应的主机:
pl_PL 对应 https://pl-pl.fitatu.com/api,de_DE 对应 https://de-de.fitatu.com/api。
fitatu-api login 会在 https://pl-pl.fitatu.com/api 上登录,并存储存储地区对应的主机;使用 --base-url 时,则会在指定地址登录并存储该 URL。
| 分组 | 请求头 | 说明 |
|---|---|---|
| 必需 | API-Key、API-Secret、Authorization: Bearer <jwt> | 密钥和密钥值是固定的应用值(缺少它们会返回 403);用户路由需要 Authorization(401)。 |
| 语义 | APP-SearchLocale、APP-Locale、APP-StorageLocale、APP-Timezone | APP-SearchLocale 和 locale 查询参数会改变搜索结果。其他请求头描述用户信息。四者均来自会话,login() 会根据个人资料填写这些值;如果会话中没有这些值,则会发送 pl_PL / Europe/Warsaw 默认值。 |
| 标识 | APP-OS、APP-Version、APP-UUID、API-Cluster、User-Agent | 仅用于标识客户端。该库始终发送一组固定的 Web 应用配置(constants.py 中的 WEB_CLIENT_PROFILE),绝不会将其存储在会话中。 |
如果 Fitatu 在该库发布新版前更改了 Web 应用发送的内容(其版本,或 API 密钥和密钥值),可将 FITATU_CLIENT_PROFILE 设置为包含 WEB_CLIENT_PROFILE 中相应键的 JSON 对象来进行修改,例如 FITATU_CLIENT_PROFILE='{"app_version": "4.14.0"}'。任何其他键或非纯文本值都会被拒绝,并引发 FitatuValidationError(此时 fitatu-mcp 不会启动)。fitatu-api doctor 会报告当前版本是否仍为最新版本。
登录(使用 _username 和 _password 发送 POST /login)和刷新(使用 {"refresh_token": ...} 发送 POST /token/refresh)都会经过相同的请求路径并发送相同的请求头,但不包含 Authorization;登录时,地区请求头仍使用 pl_PL 默认值。无需对请求签名,也不会强制执行 Firebase App Check;该库不会发送此项信息。
凭据只会发送到 fitatu.com 下的 https 主机:任何其他 base_url 或绝对 URL 都会在发出请求前引发 FitatuValidationError;Authorization 只会发送到 base_url 下的 URL 或 https://fitatu.com/api。不会跟随重定向:3xx 响应会引发 FitatuApiError,因此请求正文绝不会被发送到其他主机。
错误、重试和速率限制
该库的错误均为 FitatuApiError 的子类。describe_error(exc) 会将任意异常转换为 JSON 安全的字典,供 FitatuTools.call()(以及已弃用的 FitatuLibrary)返回。
| 异常 | 引发时机 | error_type |
|---|---|---|
FitatuValidationError(也是 ValueError) | 参数无效;未发送任何内容。 | validation |
FitatuNotFoundError | 你指定的 id、餐食或行不存在:库在读取的数据中发现了这一情况(消息会列出实际存在的内容),或服务端返回 404(status_code 为 404)。 | not_found |
FitatuAuthError | 刷新后仍返回 401、刷新令牌被拒绝、没有会话文件,或登录时电子邮件或密码错误(Fitatu 返回 422);此时 hint 会指向 import-session。 | auth |
FitatuRateLimitError | 重试后仍返回 429;retry_after 保存服务端要求等待的秒数。 | rate_limit |
FitatuConnectionError | 未收到 HTTP 响应(DNS、超时、连接重置)。 | network |
FitatuMaintenanceError | Fitatu 的 technical-break 标头;包含 display_from / display_to。 | maintenance |
FitatuPlannerSyncError | 日期同步返回 202,但拒绝了某一天;results 包含各日期对应的错误。 | sync_rejected |
FitatuPremiumRequiredError | 在没有 Premium 的账号上调用 Fitatu 应用仅向 Premium 提供的工具;未发送任何内容。 | forbidden |
FitatuApiError | 其他任何 HTTP 错误:status_code、body、codename(例如 NUMBER_OF_USES_EXCEEDED)。 | forbidden(403)、validation(400/409/422)、server |
消息格式为 METHOD /path -> status CODENAME: server message。在 describe_error() 的消息中,用户 id 会替换为 <user>(GET /diet-plan/<user>/days -> 403);其他位置出现用户 id 时也会替换为 <user>,前提是传入了 user_id=(FitatuTools.call() 会传入),这样结果就不会将其带给模型提供方。正文为 HTML 页面的 403 错误来自 API 前方的防火墙(例如 Cloudflare),并非 Fitatu 的 Premium 或密钥检查;其 hint 会说明这一点。其他任何异常(例如库内部引发的 TypeError)都属于 unexpected:应作为 Bug 报告,因为 FitatuTools.call() 会先检查参数。
重试:网络错误、429、500/502/503/504,以及 Cloudflare 的 520 至 524,会针对 GET、HEAD 和 OPTIONS 重试;带有幂等标记的写入也会重试:日期同步(所有计划器写入;按行 id 执行 upsert)、water.set_day、measurements.set_day、measurements.delete_day 和 foods.set_favorite。其他写入(目录写入、设置、登录、刷新)只发送一次。最多尝试 retry_max_attempts=3 次,采用从 retry_base_delay_seconds=0.5 开始的指数退避。Retry-After 最长等待 30 秒;如果要求等待更久,则不会等待,而是立即引发错误(对于 429,会引发带有 retry_after 的 FitatuRateLimitError)。技术维护期间不会重试或刷新。在 with client.time_limit(seconds): 中,所有请求都会在指定时间内结束:如果重试等待会导致剩余时间不足 2 秒,则不会重试;每个请求的超时都会缩短为剩余时间;如果没有剩余时间,则不会发送请求(FitatuConnectionError)。FitatuTools 会将每次调用的时间限制为 45 秒(call_timeout)。
大多数写入会发出多个请求,在估算 429 限制时需要计入这些请求:工具会在每次日记写入前读取当天数据;更新、移动、删除和活动写入会在之后再次读取,以设置 ok。每次 Agent 工具调用的请求数(不含重试和令牌刷新):
| 请求数 | 工具 |
|---|---|
| 0 | get_today |
| 1 | 其他读取工具;set_water;set_favorite;使用 dry_run 的 preview_removal 和 remove_items;删除已删除食谱的 delete_recipe、删除已删除产品的 delete_food |
| 2 | get_range_summary、get_profile、get_fasting(如果设置中通过 ID 指定方案,则为 3)、get_habits、find_by_barcode(未找到产品时为 1)、按份数添加食谱的 add_food(Fitatu Premium 标记额外计 1,这是 10 分钟内首次此类调用;没有 Fitatu Premium 时再多计 1,因为它会先读取食谱)、add_custom_item、log_weight、delete_recipe、set_targets、create_food、delete_food;add_foods,每个带有 unit 或 measure_id 的项目额外计 1,没有 Fitatu Premium 时,每个按份数添加的食谱也额外计 1(Fitatu Premium 标记额外计 1,规则同 add_food);create_recipe,每种配料额外计 1;带筛选条件但不含 max_energy_kcal 的 search_recipes,以及高级食谱的 get_recipe 和 get_food_measures,这是 FitatuTools 上 10 分钟内首次此类调用(会读取账户的 Premium 标记;之后为 1) |
| 3 | 带有 unit 或 measure_id 的 add_food(会先读取食物)、log_measurements(会从个人资料中读取尺寸单位)、当天的 copy_meal、remove_items、add_activity、remove_activity、set_note、update_food;update_recipe,每种新增配料额外计 1 |
| 4 | update_item、当天的 move_item、移至其他日期的 copy_meal |
| 5 | get_progress(任意最多 62 天的范围;没有 Fitatu Premium 时见下文) |
| 6 | 移至其他日期的 move_item |
| 8 | get_report(范围内每增加 41 次称重,额外计 1) |
| 每天 1 次 | get_shopping_list,范围内计划的每个食谱额外计 1(若有高级食谱,Premium 标记额外计 1);没有 Fitatu Premium 时的 get_progress 和 get_report,范围额外计 1(单日范围不计),以及 get_report 的称重次数;此外,FitatuTools 上 10 分钟内首次此类调用额外计 1 次,Fitatu 不允许该调用 |
隐私与日志
该库通过标准 logging 模块记录日志,且不配置处理器。fitatu-api 命令和 fitatu-mcp 将日志写入 stderr,绝不会写入 stdout(stdout 用于输出命令结果或 MCP 协议):该库的日志级别为 WARNING,也可由 --log-level DEBUG|INFO|WARNING|ERROR(对于 fitatu-api,须在命令前指定,例如 fitatu-api --log-level DEBUG call get_today)或 FITATU_LOG_LEVEL 设置;其他库的日志级别为 WARNING。
INFO(fitatu_api.client):以 JSON 行记录认证事件:登录(用户 ID)、令牌刷新开始、成功和失败,以及clear_auth,并附带非敏感状态快照(用户 ID、本地化设置、时区、主机、令牌过期时间、是否存在令牌)。WARNING(fitatu_api.client):请求在网络错误、429 或 5xx 后重试仍失败时记录fitatu.request.error;当 Fitatu 返回的新令牌看起来已过期(本地时钟不准,因此会停止提前刷新)时记录fitatu.auth.clock_skew;会话文件现在存有其他帐户的令牌(客户端会保留自己的令牌)时记录fitatu.auth.store_other_account。fitatu_api.agent、fitatu_api.facade和fitatu_api.mcp_server会在调用结果报告unexpected错误(包含回溯)、auth错误或maintenance错误时记录日志:get_day failed (auth): ...。fitatu_api.auth会在无法读取令牌存储(通常是会话文件)时记录日志。fitatu_api.mcp_server在--http模式下,会为每个因令牌问题而遭拒的请求记录日志,并附上客户端地址(绝不记录令牌);uvicorn 会另外记录启动和停止日志。DEBUG(fitatu_api.client):以 JSON 行记录每个请求:fitatu.request.start(方法、路径、查询参数,例如搜索词);每次重试等待前记录fitatu.request.retry(失败的attempt、delay_seconds和reason:状态码或网络错误类型);随后记录fitatu.request.ok或fitatu.request.error(状态码、attempt、自开始以来的elapsed_ms,以及错误正文的前 300 个字符;4xx 错误仍为 DEBUG 级别,因为调用方会将其作为异常收到,且其中一些情况(例如 404)是预期的)。fitatu_api.planner会为每次规划器写入记录一行(用户 ID、日期、餐食、食物 ID、自定义行的名称和 kcal);工具层会记录结果报告的其他所有错误。路径中包含用户 ID 和日期。
绝不会记录请求正文、密码和令牌:键名中包含 token、secret、password 或 authorization 的值会替换为 [redacted],且认证上下文的 repr 不会包含令牌和 API 密钥。错误消息最多包含服务器消息的 200 个字符;完整响应正文可在 exc.body 中获取,但 describe_error() 和 FitatuTools(以及已弃用的 FitatuLibrary)的错误结果不会包含该正文。无效的标头值令牌(例如粘贴时带有换行符)会在不引用令牌内容的情况下报告。
测试
pip install -e ".[dev]"
pytest -q
测试套件可离线运行。tests/fixtures/api_recording.json 保存了真实 API 响应(已清除个人数据),涵盖每日同步、测量数据、摘要、搜索和设置;tests/fake_server.py 是基于这些响应构建的模拟后端,若模拟数据与录制内容不一致,tests/test_recorded_contract.py 就会失败。录制数据的生成和更新方法:
tests/fixtures/README.md。
实时测试套件(tests/test_live_all.py)会对真实账户运行,并且会写入数据:
FITATU_RUN_LIVE_TESTS=1 pytest tests/test_live_all.py
它使用 fitatu-api login 写入的会话文件(或 $FITATU_SESSION),并将刷新的令牌保存回该文件;如果设置了 $FITATU_SESSION_JSON,则改用该会话,且不会保存刷新的令牌。可选项:FITATU_LIVE_DAY(今天之后的日期;默认为一年后的今天)、FITATU_LIVE_MEAL_KEY(snack)、FITATU_LIVE_PRODUCT_ID、FITATU_LIVE_RECIPE_ID、FITATU_LIVE_MEASUREMENT_DAY(2020-01-01;若当天已有测量数据则跳过)和 FITATU_LIVE_RUN_WRITE_ACCOUNT=1。
每项测试都会删除自己在 FITATU_LIVE_DAY 添加的所有条目;如果有条目残留,测试就会失败;不允许将今天或更早的日期设为测试日期。当天的饮水量和备注会恢复原状,而写入 FITATU_LIVE_MEASUREMENT_DAY 的体重和测量数据会被删除。使用 -k ReadOnly 只运行读取数据的测试。
记录目录中的食物会将其添加到账户的最近食物列表中,且不会撤销。设置 FITATU_LIVE_RUN_WRITE_ACCOUNT=1 后,测试套件还会更改并恢复水杯容量和每日目标(Fitatu 会将恢复后的目标作为从今天起生效的新版本保存),并创建后删除一个私人产品和两份私人食谱(其中一份通过工具创建,记录在 FITATU_LIVE_DAY,之后删除),还会通过工具创建一个私人产品(记录在 FITATU_LIVE_DAY,修改后删除)。产品和食谱的删除采用软删除,因此和最近食物一样,无法完全撤销:它们会保留在账户中,标记为已删除,并从你的食物列表和搜索结果中隐藏。完整清单见 tests/test_live_all.py 的模块文档字符串。切勿将 FITATU_LIVE_DAY 指向含有重要条目的日期。
scripts/check_contract.py 只读检查实时 API 是否仍以录制文件所包含的结构响应:它会输出路由和键名,绝不输出值;如果键缺失、类型发生变化或路由调用失败,则退出码为 1。“实时契约”工作流每周一会运行该脚本和只读实时测试,使用临时账户的会话(存于 FITATU_SESSION_JSON 密钥中);如果未提供会话,则跳过:
CONTRIBUTING.md。
scripts/eval_agent.py 会评估 Claude 模型在示例饮食日记中使用工具的表现(波兰语和英语共 29 项任务;需要 ANTHROPIC_API_KEY,且会产生 API 用量费用,因此 CI 不会运行):docs/AGENTS.md。
文档
- docs/AGENTS.md:面向 Agent 的工具、安全写入、错误处理和 MCP 服务。
- docs/PLANNER.md:每日同步约定、ID,以及每次日志写入的结果。
- docs/API_REFERENCE.md:所有公开类和方法及其稳定性。
- docs/APP_4_17_1_API_CATALOG.md:通过反编译获取的 Fitatu Android 应用 4.17.1 的所有路由。
- docs/RELEASING.md:维护者的发布步骤。
- CHANGELOG.md
Bug 和问题:GitHub issues。安全 问题请私下报告:SECURITY.md。 拉取请求的设置、检查和规则: CONTRIBUTING.md。
许可证
MIT,详见 LICENSE。此许可证仅适用于本 代码,不适用于 Fitatu 服务、其 API 或其返回的数据。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。