mcpField
F
Fitatu

Fitatu

本地服务
github.com健康医疗21今天更新

关于 Fitatu

Fitatu 是用于读取和更新 Fitatu 账户饮食日记及相关健康数据的非官方 Python 客户端和 MCP 服务,面向脚本与 AI Agent,提供结构化工具和命令行操作。

主要功能

  1. 01读取和编辑饮食日记
  2. 02记录活动、身体测量和饮水
  3. 03搜索食物、食谱和活动
  4. 04读取摘要、目标和习惯
  5. 05管理会话、工具及导出的命令行

适用场景

  1. 01通过 AI Agent 记录餐食
  2. 02查看体重、饮水和运动记录
  3. 03汇总指定日期范围的日记数据

MCP 相关

`fitatu-mcp` 将 Agent 工具通过标准输入输出或 HTTP 提供给 MCP 主机,也支持只读和演示模式。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"jezue-fitatu-api": {
"command": "uvx",
"args": [
"fitatu-api"
],
"env": {
"FITATU_SESSION": "<FITATU_SESSION>"
}
}
}
}
启动命令uvx fitatu-api
需要配置的环境变量
FITATU_SESSIONSession file written by `fitatu-api login`; default ~/.fitatu/session.json.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypifitatu-api0.6.0

工具

47

收录的工具列表 · 更新于 2 周前

此工具无需参数。

详细介绍

fitatu-api:为 AI Agent 提供的 Fitatu 饮食日记(非官方)

fitatu-api

PyPI Python Status License CI Fitatu MCP on mcprush

这是一个非官方的 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_recipeget_my_foods;create_food、update_food、delete_food,以及仅用于用户自己的私有食物的食谱工具
测量数据client.measurements:每天的体重和身体尺寸、历史记录、图表get_weight_history、get_body_measurements、get_measurement_history、log_weight、log_measurements
饮水client.waterget_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 应用复制会话:

    1. 使用 Apple 或 Facebook 登录 https://www.fitatu.com/app/。

    2. 在该页面打开浏览器的开发者控制台,并运行下面这行代码。它只会复制令牌和你的用户 ID;Web 应用的 localStorage.user 还包含你的电子邮件、昵称等信息。

      copy(JSON.stringify((u => ({token: u.token, refresh_token: u.refresh_token, id: u.id}))(JSON.parse(localStorage.user))))
      
    3. 在 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-TimezoneAPP-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
FitatuMaintenanceErrorFitatu 的 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 工具调用的请求数(不含重试和令牌刷新):

请求数工具
0get_today
1其他读取工具;set_water;set_favorite;使用 dry_run 的 preview_removal 和 remove_items;删除已删除食谱的 delete_recipe、删除已删除产品的 delete_food
2get_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
4update_item、当天的 move_item、移至其他日期的 copy_meal
5get_progress(任意最多 62 天的范围;没有 Fitatu Premium 时见下文)
6移至其他日期的 move_item
8get_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。

文档

Bug 和问题:GitHub issues。安全 问题请私下报告:SECURITY.md。 拉取请求的设置、检查和规则: CONTRIBUTING.md。

许可证

MIT,详见 LICENSE。此许可证仅适用于本 代码,不适用于 Fitatu 服务、其 API 或其返回的数据。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。