Fitatu

Servidor MCP no oficial para Fitatu, el contador de calorías polaco: lee y registra alimentos, peso, agua y entrenamientos, consulta las calorías y macros restantes, y obtén informes semanales. 40 herramientas (23 de solo lectura), modos de solo lectura y demo, cuentas gratuitas y Premium. Python, en PyPI: uvx --from "fitatu-api[mcp]" fitatu-mcp

Documentación

fitatu-api: your Fitatu food diary for AI agents (unofficial)

fitatu-api

PyPI Python Status License CI

Un cliente Python no oficial para la API de Fitatu, el contador de calorías, diseñado para ser utilizado por agentes de IA y scripts. Lee y escribe el diario de alimentos, actividades, mediciones corporales y agua, lee y establece objetivos, y lee resúmenes y el catálogo de alimentos. FitatuTools convierte el diario en herramientas que un modelo de lenguaje puede llamar. El manejo de sesión sigue a python-garminconnect: inicia sesión una vez, mantén un archivo de sesión, deja que el cliente lo actualice.

No oficial. Este proyecto no está afiliado ni respaldado por Fitatu Sp. z o.o. La API fue ingeniada inversamente a partir de las aplicaciones de Fitatu y puede cambiar sin previo aviso. El acceso automatizado puede violar los Términos de Servicio de Fitatu; revísalos y usa la biblioteca solo con tu propia cuenta y bajo tu propio riesgo. La licencia MIT cubre este código, no el servicio de Fitatu, su API ni sus datos.

Instalación

Python 3.11 o superior; la única dependencia es requests (y tzdata en Windows).

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

Para trabajar en la biblioteca o ejecutar sus pruebas sin conexión, clónala en su lugar: git clone https://github.com/Jezue/fitatu_library && cd fitatu_library && pip install -e ".[dev]".

Inicio rápido

fitatu-api login   # asks for e-mail and password, writes ~/.fitatu/session.json

¿Te registraste con Apple o Facebook? Entonces la cuenta no tiene contraseña: copia la sesión desde la aplicación web con fitatu-api import-session en su lugar, como se describe en Autenticación y archivo de sesión.

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"])

Cómo funcionan las escrituras, los IDs de fila y las eliminaciones: docs/PLANNER.md.

Para copiar el menú de una dieta de catering al diario (seguro de ejecutar de nuevo), usa catering_sync.py desde el repositorio: Importar un menú de catering.

Para agentes de IA

FitatuTools expone 47 herramientas (leer el día, buscar alimentos, registrar alimentos, peso, agua, entrenamientos, recetas y productos privados, ...) con entradas de JSON Schema y anotaciones de solo lectura / destructivas / idempotentes. call() nunca lanza excepciones: devuelve {"ok": true, "data": ...} o {"ok": false, "error": {"error_type", "message", "retryable", "hint", ...}}, para que el modelo pueda leer qué salió mal. SYSTEM_PROMPT es el prompt de sistema recomendado: las reglas que el modelo necesita además de las descripciones de las herramientas (leer antes de escribir, nombres de comidas propios del usuario, data.ok, confirmaciones). El modelo pregunta a get_today por la fecha, en la zona horaria de la cuenta.

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"))

Las herramientas de escritura (registrar alimentos, actualizar, mover, copiar y eliminar filas, peso, agua, entrenamientos, recetas privadas y productos) necesitan un bucle que pregunte al usuario antes de llamadas destructivas; está en docs/AGENTS.md, con la tabla de herramientas, las llamadas que nunca deben exponerse (tokens de sesión, escrituras en el catálogo), cómo hacer que las escrituras sean seguras de repetir, qué hacer por error_type, la variante de OpenAI, hosts de larga duración y cómo probar las herramientas sin una cuenta contra el backend de pruebas sin conexión.

MCP

fitatu-mcp sirve las mismas herramientas a Claude Code, Claude Desktop y otros hosts MCP a través de stdio, con SYSTEM_PROMPT como sus instrucciones y tres comandos de barra en polaco (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 sirve las mismas herramientas a través de HTTP, con un token, para n8n, otra computadora o Docker (el repositorio tiene un Dockerfile): docs/AGENTS.md.

Sin una cuenta, fitatu-mcp --demo (o FitatuTools.demo()) sirve un pequeño diario ficticio mantenido en memoria. Para Claude Desktop, cada lanzamiento de GitHub tiene fitatu-<version>.mcpb, instalado con un doble clic (inicia sesión con fitatu-api login primero). Sin instalar nada, uvx --from "fitatu-api[mcp]" fitatu-mcp ejecuta el servidor. Está listado en el Registro MCP como io.github.Jezue/fitatu-api (server.json). Para Hermes Agent y otros hosts que leen Agent Skills, skills/fitatu-diary/SKILL.md le dice al agente cómo configurar el servidor y usar bien sus herramientas.

El prompt de permiso del host es el paso de confirmación, así que mantenlo activado para las herramientas destructivas. Claude Desktop, uvx y lo que hace el servidor: docs/AGENTS.md.

Qué cubre

ÁreaCliente (FitatuApiClient)Herramientas de agente
Diario de alimentosclient.planner: leer un día, agregar productos, recetas, filas personalizadas y resultados de búsqueda, actualizar, mover, copiar, eliminar; la nota del díaget_day, add_food, add_foods, add_custom_item, update_item, move_item, copy_meal, preview_removal, remove_items, set_note
Actividadesplanner.add_activity / remove_activity, client.activities (catálogo, búsqueda, recientes)search_activity, add_activity, remove_activity
Alimentossearch_food, get_product_details, get_recipe, catálogo de recetas y su búsqueda, client.foods (favoritos, recientes)search_food, search_recipes, get_recipe, find_by_barcode, get_food_measures, get_recent_foods, get_favorite_foods, set_favorite
Alimentos propios del usuariosearch_user_food, create_product, update_product, delete_product, create_recipe, update_recipe, delete_recipeget_my_foods; create_food, update_food, delete_food y las herramientas de recetas solo para los alimentos privados propios del usuario
Medicionesclient.measurements: peso y medidas corporales por día, historial, gráficosget_weight_history, get_body_measurements, get_measurement_history, log_weight, log_measurements
Aguaclient.waterget_water, set_water
Resumen y exportaciónclient.summary: totales por rango, nutrientes por día, productos principales, exportación del diarioget_range_summary, get_progress, get_top_foods, get_report, get_shopping_list (calculado a partir de las filas planificadas)
Ayuno y hábitosclient.fasting (esquemas, períodos de comida por día), client.habits (catálogo, hábitos del usuario, buenos días); solo lecturaget_fasting, get_habits, get_good_days
Objetivos y configuraciónclient.user_settings: objetivos, BMR/TMR, perfilget_targets, set_targets (el objetivo manual o automático, división de macronutrientes, tamaño del vaso), get_profile (altura, peso, objetivo, IMC, Premium; nada que identifique al usuario)
Escrituras en el catálogocreate_recipe(shared=True), set_product_proposal, limpieza de duplicadosninguna, a propósito

FitatuLibrary, que envuelve la mayoría de estos como métodos *_via_api que devuelven {"status": ...} en lugar de lanzar excepciones, está obsoleto y se eliminará en 1.0: para resultados que nunca lanzan excepciones usa FitatuTools ({"ok": true, "data": ...}), de lo contrario el cliente. Cada llamada pública con su estabilidad está listada en docs/API_REFERENCE.md.

Cuentas gratuitas y Premium

Cuenta gratuitaPremium
Diario: leer, registrar, actualizar, mover, copiar, eliminar alimentos, notas, actividades, agua, peso, mediciones corporales✅✅
Búsqueda, código de barras, medidas de alimentos, búsqueda de recetas por palabras y max_energy_kcal, recetas completas, recetas y productos privados, favoritos, alimentos recientes✅✅
Filtros de búsqueda de recetas (min_protein_g, max_carbohydrate_g, max_cooking_time, diets, meal) y recetas premium del catálogo (get_recipe, get_food_measures, add_food, add_foods; la lista de compras omite sus ingredientes)❌ "necesita Fitatu Premium": las herramientas siguen la aplicación✅
Objetivos: leer y establecer (get_targets, set_targets), progreso (get_progress), informes (get_report), totales por rango (get_range_summary)✅ (progreso: una solicitud por día, como máximo 31 días)✅
Historial de peso y mediciones corporales, perfil, ayuno y hábitos (lectura), lista de compras✅✅
Alimentos principales (get_top_foods, summary.get_top_products())❌ 403: necesita Fitatu Premium✅
Exportación del diario (fitatu-api export, summary.export_diary())❌ 403: "necesita Fitatu Premium"✅
Proteína / grasa / carbohidratos día a día (summary.get_nutrient_by_day(); la energía funciona en ambos)❌ 403 (get_progress lo lee alrededor)✅

Casi todo funciona en una cuenta sin Fitatu Premium. Fitatu responde 403 sin él a (verificado en vivo 2026-10-15):

  • summary.get_nutrient_by_day() para proteína, grasa y carbohidratos (la energía funciona en cualquier cuenta);
  • summary.get_top_products(), y por lo tanto la herramienta get_top_foods;
  • summary.export_diary(), y por lo tanto fitatu-api export, que entonces lo dice en una línea.

Fitatu sirve los filtros de búsqueda de recetas y las recetas premium del catálogo a cualquier cuenta, pero su aplicación los mantiene para Premium, y también las herramientas: verifican los indicadores accessControl de la cuenta y responden con un error forbidden sin ellos. El cliente en sí no verifica.

get_progress y get_report funcionan en todas las cuentas: cuando se rechaza la ruta día a día de macronutrientes, leen el resumen de cada día en su lugar (una solicitud por día, como máximo 31 días), y get_report da top_foods nulo con un top_foods_note. Los objetivos de macronutrientes de una cuenta gratuita son un rango (target_min por debajo de target_max); en una cuenta Premium un resumen de un día dio los dos iguales (la ruta día a día get_progress lee allí está de acuerdo, verificado en vivo). También toma un objetivo manual y su propia división de macronutrientes (set_targets, verificado en vivo 2026-10-15); con una división, sus objetivos de macronutrientes son números únicos. fitatu-api doctor dice si la cuenta tiene un plan premium. Ver Cuentas gratuitas y Premium en docs/AGENTS.md para las herramientas.

Línea de comandos

fitatu-api (también python -m fitatu_api) funciona en el archivo de sesión, $FITATU_SESSION o ~/.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 imprime de dónde viene la sesión (el archivo y sus permisos, o $FITATU_SESSION_JSON), el ID de usuario, el host de la API, las configuraciones regionales, cuándo expira el token de portador y si hay un token de actualización, sin enviar nada; --check también lee el perfil una vez. doctor verifica la configuración con Fitatu, una línea ok / WARN / FAIL cada una: los permisos del archivo de sesión, el token de actualización (actualiza el token de portador una vez y lo guarda, como cualquier actualización), el reloj de esta computadora contra el tiempo de emisión del token nuevo, una lectura de perfil, si un plan premium está activo (no hasta cuándo, ni su precio), y si la aplicación web de Fitatu sigue en la versión que la biblioteca envía (GET /upgrade-application/WEB/<version>); sale con 1 cuando una verificación falla y no imprime ningún token, ID de usuario o directorio de inicio, para que su salida pueda ir a un informe de errores. logout elimina el archivo de sesión; detén fitatu-mcp y otros procesos que lo usen primero, ya que escribirían sus tokens actualizados de vuelta. Fitatu no tiene una llamada que la biblioteca conozca que revoque tokens. Ninguno de estos comandos imprime un token. fitatu-api --version imprime la versión.

call ejecuta una herramienta a través de FitatuTools.call() e imprime su envoltorio como JSON; el estado de salida es 0 cuando ok es verdadero y 1 cuando es falso. Solo las herramientas de lectura (READ_ONLY_TOOLS) se ejecutan a menos que se dé --allow-writes. export guarda la exportación del diario de Fitatu (csv, xls, pdf o txt) en un archivo nuevo legible solo por ti (0600); no reemplaza un archivo existente sin --overwrite, y un rango sin nada registrado no escribe ningún archivo y sale con 1. fitatu-api COMMAND --help describe cada comando. Los registros van a stderr, desde WARNING a menos que fitatu-api --log-level DEBUG COMMAND o FITATU_LOG_LEVEL diga lo contrario (ver Privacidad y registro).

Autenticación y archivo de sesión

fitatu-api login [--email ADDRESS] [-o PATH] [--base-url URL] (también python -m fitatu_api login) inicia sesión, lee tu perfil para las configuraciones regionales y la zona horaria de la cuenta, y escribe el archivo de sesión. Lee FITATU_EMAIL y FITATU_PASSWORD cuando están configurados y solicita de lo contrario. Las cuentas sin contraseña usan fitatu-api import-session (abajo).

  • Dónde. fitatu-api login, fitatu-api import-session, FitatuApiClient.from_session(), FitatuTools.from_session() y el obsoleto FitatuLibrary.from_session() usan el mismo archivo: la ruta que pasas (-o para la CLI), si no $FITATU_SESSION, si no $FITATU_SESSION_PATH, si no ~/.fitatu/session.json. Un archivo faltante lanza FitatuAuthError indicándote que ejecutes fitatu-api login.

  • Qué. JSON con bearer_token, refresh_token, fitatu_user_id, base_url (el host de la API de la región), app_locale, app_searchlocale, app_storagelocale, app_timezone y una versión de esquema. La identidad del cliente (clave de API, versión de la app, ...) no se almacena; proviene de la biblioteca.

  • Permisos. El archivo se escribe solo para el propietario (0600), en un directorio creado con 0700, mediante un archivo temporal que lo reemplaza, de modo que un lector nunca ve media sesión. Contiene tokens en vivo: trátalo como una contraseña.

  • Tokens. Verificados en vivo el 2026-10-13: el token de portador es un JWT que dura una hora. El token de actualización es una cadena opaca que Fitatu no rota: POST /token/refresh devuelve un nuevo token de portador y el mismo token de actualización, que sigue funcionando, y los tokens de portador más antiguos siguen siendo válidos hasta que expiren. POST /login responde a un correo o contraseña incorrectos con 422, no 401.

  • Actualización y reescritura. El cliente actualiza el token de portador 60 segundos antes de que expire (del JWT exp) y una vez después de un 401, una actualización a la vez. Los tokens se fusionan en el archivo de sesión, de modo que el siguiente proceso comienza con ellos; un proceso que comparte el archivo recoge un par que otro guardó, p. ej. después de que fitatu-api login se ejecutara de nuevo. Pasa persist_tokens=False para mantener el archivo intacto. Un par de otra cuenta (un inicio de sesión en otra cuenta con el mismo archivo) no se recoge: el cliente en ejecución conserva sus propios tokens y deja de escribir en el archivo, de modo que ese inicio de sesión permanece. Cuando un token recién obtenido de Fitatu ya parece expirado, el reloj de la computadora va rápido: el cliente entonces registra una advertencia y actualiza solo después de un 401.

  • Iniciar sesión de nuevo. Cuando Fitatu rechaza el token de actualización, las llamadas lanzan FitatuAuthError (error_type auth). Ejecuta fitatu-api login (o import-session) de nuevo; un cliente en ejecución recoge el nuevo par del archivo la próxima vez que necesite una actualización.

  • Cuentas sin contraseña. Las cuentas creadas con Apple o Facebook (incluidas las direcciones "Ocultar mi correo" de Apple) no tienen contraseña para fitatu-api login. Copia la sesión de la aplicación web en su lugar:

    1. Inicia sesión en https://www.fitatu.com/app/ con Apple o Facebook.

    2. Abre la consola de desarrollador del navegador en esa página y ejecuta la línea siguiente. Copia solo los tokens y tu id de usuario; el localStorage.user de la aplicación web también contiene tu correo, apodo y más.

      copy(JSON.stringify((u => ({token: u.token, refresh_token: u.refresh_token, id: u.id}))(JSON.parse(localStorage.user))))
      
    3. En macOS ejecuta pbpaste | fitatu-api import-session. En Linux o Windows pégala en un archivo, ejecuta fitatu-api import-session < file (PowerShell: Get-Content file | fitatu-api import-session) y elimina el archivo.

    import-session [-o PATH] [--base-url URL] lee el JSON desde stdin (un archivo de sesión de esta biblioteca también funciona), verifica ambos tokens con Fitatu actualizando el portador una vez (así que el guardado es válido durante una hora), lee las configuraciones regionales y la zona horaria de la cuenta como login y escribe el archivo de sesión de la misma manera. Nada más de la entrada se guarda. El token es una credencial completa de la cuenta: no lo pegues en ningún otro lugar.

  • Contenedores y cron. Donde no quepa un archivo de sesión escribible, coloca el JSON de sesión en sí en FITATU_SESSION_JSON: fitatu-api login --print (o import-session --print) lo imprime en stdout en lugar de escribir un archivo, para un almacén de secretos. from_session() sin una ruta lo toma antes que cualquier archivo y mantiene los tokens que actualiza en memoria: Fitatu no rota el token de actualización, así que el mismo valor funciona para cada ejecución hasta que el token de actualización en sí expire (cuánto dura aún no se sabe) o inicies sesión de nuevo.

  • Fachada (obsoleta). FitatuLibrary.from_session() carga el mismo archivo y escribe los tokens actualizados de vuelta en él. FitatuLibrary(session_data, session_path=...) hace lo mismo para un dict de sesión y copia el par actual en session_data después de cada llamada.

  • Desde Python. FitatuApiClient.login(email, password, token_store_path=path) inicia sesión y fusiona toda la sesión en path.

Regiones y encabezados

El host de la API es el base_url de la sesión, si no el host de la configuración regional de almacenamiento de la cuenta: pl_PL se convierte en https://pl-pl.fitatu.com/api, de_DE se convierte en https://de-de.fitatu.com/api. fitatu-api login inicia sesión en https://pl-pl.fitatu.com/api y almacena el host de la configuración regional de almacenamiento; con --base-url inicia sesión allí y almacena esa URL.

GrupoEncabezadosNotas
RequeridosAPI-Key, API-Secret, Authorization: Bearer <jwt>La clave y el secreto son valores estáticos de la app (403 sin ellos); Authorization es necesario en rutas de usuario (401).
SemánticosAPP-SearchLocale, APP-Locale, APP-StorageLocale, APP-TimezoneAPP-SearchLocale y el parámetro de consulta locale cambian los resultados de búsqueda. Los otros describen al usuario. Los cuatro provienen de la sesión, que login() llena desde el perfil; una sesión sin ellos envía los valores predeterminados pl_PL / Europe/Warsaw.
IdentificaciónAPP-OS, APP-Version, APP-UUID, API-Cluster, User-AgentSolo identifican al cliente. La biblioteca siempre envía un perfil constante de la aplicación web (WEB_CLIENT_PROFILE en constants.py), nunca almacenado en sesiones.

Si Fitatu cambia lo que la aplicación web envía (su versión, o la clave de API y el secreto) antes de que una versión de la biblioteca lo siga, establece FITATU_CLIENT_PROFILE a un objeto JSON con las claves de WEB_CLIENT_PROFILE para cambiar, p. ej. FITATU_CLIENT_PROFILE='{"app_version": "4.14.0"}'. Cualquier otra clave o un valor que no sea texto plano se rechaza con un FitatuValidationError (fitatu-mcp entonces no se inicia). fitatu-api doctor indica si la versión sigue siendo la más reciente.

El inicio de sesión (POST /login con _username y _password) y la actualización (POST /token/refresh con {"refresh_token": ...}) pasan por la misma ruta de solicitud y envían los mismos encabezados, sin Authorization; en el inicio de sesión, los encabezados de configuración regional siguen siendo los valores predeterminados pl_PL. No hay firma de solicitudes y Firebase App Check no se aplica; la biblioteca no lo envía.

Las credenciales solo van a hosts https bajo fitatu.com: cualquier otro base_url o URL absoluta lanza FitatuValidationError antes de que se haga una solicitud, y Authorization solo se envía a URLs bajo base_url o https://fitatu.com/api. Las redirecciones no se siguen: una respuesta 3xx es un FitatuApiError, así que un cuerpo de solicitud nunca pasa a otro host.

Errores, reintentos, límites de tasa

Los errores de la biblioteca son subclases de FitatuApiError. describe_error(exc) convierte cualquier excepción en el dict seguro para JSON que FitatuTools.call() (y el obsoleto FitatuLibrary) devuelven.

ExcepciónSe lanza cuandoerror_type
FitatuValidationError (también un ValueError)Un argumento es inválido; no se envió nada.validation
FitatuNotFoundErrorEl id, la comida o la fila que nombraste no está: la biblioteca lo encontró en lo que leyó (el mensaje lista lo que hay), o el servidor respondió 404 (status_code 404).not_found
FitatuAuthError401 después de una actualización, un token de actualización rechazado, sin archivo de sesión, o un correo o contraseña incorrectos en el inicio de sesión (Fitatu responde 422); hint entonces apunta a import-session.auth
FitatuRateLimitError429 después de reintentos; retry_after contiene la espera del servidor en segundos.rate_limit
FitatuConnectionErrorSin respuesta HTTP (DNS, tiempo de espera, restablecimiento de conexión).network
FitatuMaintenanceErrorEl encabezado technical-break de Fitatu; display_from / display_to.maintenance
FitatuPlannerSyncErrorLa sincronización del día respondió 202 pero rechazó un día; results tiene los errores por día.sync_rejected
FitatuPremiumRequiredErrorUna herramienta para lo que la app de Fitatu guarda para Premium, en una cuenta sin ello; no se envió nada para eso.forbidden
FitatuApiErrorCualquier otro error HTTP: status_code, body, codename (p. ej. NUMBER_OF_USES_EXCEEDED).forbidden (403), validation (400/409/422), server

Los mensajes leen METHOD /path -> status CODENAME: server message. En el mensaje de describe_error() el id de usuario es <user> (GET /diet-plan/<user>/days -> 403), y con user_id= (que FitatuTools.call() pasa) donde más aparezca, de modo que un resultado no lo lleva al proveedor del modelo. Un 403 cuyo cuerpo es una página HTML proviene de un firewall frente a la API (p. ej. Cloudflare), no de las verificaciones premium o de clave de Fitatu, y su hint lo dice. Cualquier otra excepción, como un TypeError lanzado dentro de la biblioteca, es unexpected: un error para reportar, ya que FitatuTools.call() verifica los argumentos primero.

Reintentos: los errores de red, 429, 500/502/503/504 y los 520 a 524 de Cloudflare se reintentan durante GET, HEAD y OPTIONS, y para escrituras marcadas como idempotentes: la sincronización del día (todas las escrituras del planificador; hace upsert por id de fila), water.set_day, measurements.set_day, measurements.delete_day y foods.set_favorite. Otras escrituras (escrituras de catálogo, configuraciones, inicio de sesión, actualización) se envían una vez. Hasta retry_max_attempts=3 intentos con retroceso exponencial desde retry_base_delay_seconds=0.5. Un Retry-After de hasta 30 segundos se respeta; uno más largo no se espera y el error se lanza de inmediato (FitatuRateLimitError con retry_after para un 429). Durante una pausa técnica no se reintenta ni se actualiza nada. Dentro de with client.time_limit(seconds): las solicitudes terminan dentro de ese tiempo en total: un reintento cuya espera dejaría menos de 2 segundos no se hace, el tiempo de espera de cada solicitud se reduce al tiempo restante, y una solicitud sin tiempo restante no se envía (FitatuConnectionError). FitatuTools ejecuta cada llamada en uno de 45 segundos (call_timeout).

La mayoría de las escrituras hacen varias solicitudes, lo que cuenta cuando presupuestas contra 429: las herramientas leen el día antes de cada escritura de diario, y las actualizaciones, movimientos, eliminaciones y escrituras de actividad lo leen de nuevo después para establecer ok. Solicitudes por llamada de herramienta del agente, antes de reintentos y actualizaciones de tokens:

SolicitudesHerramientas
0get_today
1las otras herramientas de lectura; set_water; set_favorite; preview_removal y remove_items con dry_run; delete_recipe de una receta ya eliminada, delete_food de un producto ya eliminado
2get_range_summary, get_profile, get_fasting (3 cuando la configuración nombra el esquema por id), get_habits, find_by_barcode (1 sin una coincidencia de producto), add_food para una receta en porciones (más 1 para los indicadores Premium, la primera llamada de este tipo en 10 minutos; sin Fitatu Premium, 1 más, ya que lee la receta primero), add_custom_item, log_weight, delete_recipe, set_targets, create_food, delete_food; add_foods, más 1 por cada elemento con unit o measure_id y, sin Fitatu Premium, por cada receta en porciones (y 1 para los indicadores Premium como en add_food); create_recipe, más 1 por cada ingrediente; search_recipes con un filtro pero max_energy_kcal, y get_recipe y get_food_measures de una receta premium, la primera llamada de este tipo en un FitatuTools en 10 minutos (lee los indicadores Premium de la cuenta; 1 después)
3add_food con unit o measure_id (lee el alimento primero), log_measurements (lee la unidad de tamaño del perfil), copy_meal dentro de un día, remove_items, add_activity, remove_activity, set_note, update_food; update_recipe, más 1 por cada ingrediente nuevo
4update_item, move_item dentro de un día, copy_meal a otro día
5get_progress (cualquier rango de hasta 62 días; sin Fitatu Premium, ver más abajo)
6move_item a otro día
8get_report (uno más por cada 41 pesajes adicionales en el rango)
1 al díaget_shopping_list, más 1 por cada receta planificada en el rango (y 1 para los indicadores Premium cuando una es premium); get_progress y get_report sin Fitatu Premium, más 1 para el rango (ninguno para un solo día) y los pesajes de get_report, y la primera llamada de este tipo en un FitatuTools en 10 minutos, 1 más, que Fitatu rechaza

Privacidad y registro

La biblioteca registra a través del módulo estándar logging y no configura manejadores. El comando fitatu-api y fitatu-mcp registran en stderr, nunca en stdout (que lleva el resultado del comando, o el protocolo MCP): la biblioteca desde WARNING, o desde el nivel que --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 establece; otras bibliotecas desde WARNING.

  • INFO (fitatu_api.client): eventos de autenticación como líneas JSON: inicio de sesión (id de usuario), inicio de refresco de token, éxito y fallo, y clear_auth, con una instantánea de estado no secreta (id de usuario, locales, zona horaria, host, expiración del token, si los tokens están presentes).
  • WARNING (fitatu_api.client): fitatu.request.error para una solicitud que falló definitivamente por un error de red, un 429 o un 5xx, después de sus reintentos; fitatu.auth.clock_skew (un token fresco de Fitatu parece expirado, por lo que el reloj local está desajustado; refrescar antes de la expiración se detiene) y fitatu.auth.store_other_account (el archivo de sesión ahora contiene los tokens de otra cuenta; el cliente mantiene los suyos). Desde fitatu_api.agent, fitatu_api.facade y fitatu_api.mcp_server, una llamada cuyo resultado informa un error unexpected (con el traceback), un error auth o un error maintenance: get_day failed (auth): .... Desde fitatu_api.auth, un almacén de tokens (generalmente el archivo de sesión) que no se pudo leer. Desde fitatu_api.mcp_server bajo --http, cada solicitud rechazada por su token, con la dirección del cliente (nunca el token); uvicorn agrega sus líneas de inicio y detención.
  • DEBUG (fitatu_api.client): cada solicitud como líneas JSON: fitatu.request.start (método, ruta, parámetros de consulta como frases de búsqueda), fitatu.request.retry antes de la espera de cada reintento (el attempt fallido, delay_seconds, y el reason: el estado o el tipo de error de red), luego fitatu.request.ok o fitatu.request.error (estado, attempt, elapsed_ms desde el inicio, y los primeros 300 caracteres de un cuerpo de error; un 4xx permanece en DEBUG, ya que el llamador lo recibe como una excepción y algunos, como un 404, son esperados). Desde fitatu_api.planner, una línea por escritura del planificador (id de usuario, día, comida, id de alimento, el nombre y kcal de una fila personalizada); desde las capas de herramientas, cualquier otro error que un resultado informe. Las rutas contienen el id de usuario y las fechas.

Los cuerpos de solicitud, contraseñas y tokens nunca se registran: los valores bajo claves que contienen token, secret, password o authorization se reemplazan con [redacted], y el contexto de autenticación mantiene los tokens y el secreto de la API fuera de su repr. Los mensajes de error llevan hasta 200 caracteres del mensaje del servidor; el cuerpo completo de la respuesta está en exc.body, que describe_error() y los resultados de error de FitatuTools (y el obsoleto FitatuLibrary) omiten. Un token que no es un valor de encabezado válido (por ejemplo, pegado con un salto de línea) se informa sin citarlo.

Pruebas

pip install -e ".[dev]"
pytest -q

La suite se ejecuta sin conexión. tests/fixtures/api_recording.json contiene respuestas reales de la API (depuradas de datos personales) para la sincronización diaria, mediciones, resúmenes, búsqueda y configuración; tests/fake_server.py es un backend falso construido a partir de ellas, y tests/test_recorded_contract.py falla si el falso se desvía de la grabación. Cómo se hace y se actualiza la grabación: tests/fixtures/README.md.

La suite en vivo (tests/test_live_all.py) se ejecuta contra una cuenta real y escribe en ella:

FITATU_RUN_LIVE_TESTS=1 pytest tests/test_live_all.py

Usa el archivo de sesión que fitatu-api login escribió (o $FITATU_SESSION) y guarda los tokens refrescados de nuevo en él; con $FITATU_SESSION_JSON configurado, esa sesión se usa en su lugar y los tokens refrescados no se guardan. Opcional: FITATU_LIVE_DAY (después de hoy; por defecto un año desde hoy), FITATU_LIVE_MEAL_KEY (snack), FITATU_LIVE_PRODUCT_ID, FITATU_LIVE_RECIPE_ID, FITATU_LIVE_MEASUREMENT_DAY (2020-01-01; se omite si tiene una medición) y FITATU_LIVE_RUN_WRITE_ACCOUNT=1.

Cada prueba elimina exactamente las filas que agregó en FITATU_LIVE_DAY y falla si queda una; un día en o antes de hoy se rechaza. El agua y la nota de ese día se restablecen, y el peso y las mediciones escritas en FITATU_LIVE_MEASUREMENT_DAY se eliminan. -k ReadOnly ejecuta solo las pruebas que leen. Registrar alimentos del catálogo los agrega a los alimentos recientes de la cuenta, lo que no se deshace. Con FITATU_LIVE_RUN_WRITE_ACCOUNT=1 la suite también cambia y restaura el tamaño del vaso de agua y los objetivos diarios (Fitatu mantiene los objetivos restaurados como una nueva versión desde hoy), y crea, luego elimina, un producto privado y dos recetas privadas (una a través de las herramientas, registrada en FITATU_LIVE_DAY y eliminada) y un producto privado a través de las herramientas (registrado en FITATU_LIVE_DAY, cambiado, eliminado). Las eliminaciones de productos y recetas son suaves, por lo que como los alimentos recientes no se deshacen por completo: permanecen en la cuenta, marcados como eliminados y ocultos de tus alimentos y búsquedas. La lista completa está en el docstring del módulo de tests/test_live_all.py. Nunca apuntes FITATU_LIVE_DAY a un día con entradas que te importen.

scripts/check_contract.py indica, solo leyendo, si la API en vivo todavía responde con la estructura que la grabación contiene: imprime nombres de rutas y claves, nunca valores, y sale con 1 cuando una clave falta, un tipo cambia o una ruta falla. El flujo de trabajo Live contract lo ejecuta y las pruebas en vivo de solo lectura cada lunes con la sesión de una cuenta desechable en el secreto FITATU_SESSION_JSON, y se omite sin él: CONTRIBUTING.md.

scripts/eval_agent.py puntúa qué tan bien un modelo de Claude usa las herramientas en el diario de demostración (29 tareas en polaco e inglés; necesita ANTHROPIC_API_KEY y cuesta uso de API, por lo que CI no lo ejecuta): docs/AGENTS.md.

Documentación

Errores y preguntas: problemas de GitHub. Los problemas de seguridad se informan de forma privada: SECURITY.md. Configuración, comprobaciones y reglas para solicitudes de extracción: CONTRIBUTING.md.

Licencia

MIT, ver LICENSE. Cubre este código solamente, no el servicio de Fitatu, su API o los datos que devuelve.