Fitatu

Servidor MCP não oficial para o Fitatu, o contador de calorias polonês: leia e registre alimentos, peso, água e treinos, verifique calorias e macronutrientes restantes e obtenha relatórios semanais. 40 ferramentas (23 somente leitura), modos somente leitura e demonstração, contas gratuitas e Premium. Python, no PyPI: uvx --from "fitatu-api[mcp]" fitatu-mcp

Documentação

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

fitatu-api

PyPI Python Status License CI

Um cliente Python não oficial para a API do Fitatu, o contador de calorias, criado para ser usado por agentes de IA e scripts. Ele lê e escreve no diário alimentar, atividades, medidas corporais e água, lê e define metas, e lê resumos e o catálogo de alimentos. FitatuTools transforma o diário em ferramentas que um modelo de linguagem pode chamar. O gerenciamento de sessão segue o python-garminconnect: faça login uma vez, mantenha um arquivo de sessão, deixe o cliente atualizá-lo.

Não oficial. Este projeto não é afiliado ou endossado pela Fitatu Sp. z o.o. A API foi engenharia reversa dos aplicativos Fitatu e pode mudar sem aviso. O acesso automatizado pode violar os Termos de Serviço da Fitatu; verifique-os e use a biblioteca apenas com sua própria conta e por sua conta e risco. A licença MIT cobre este código, não o serviço Fitatu, sua API ou seus dados.

Instalação

Python 3.11 ou mais recente; a única dependência é requests (e tzdata no 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 trabalhar na biblioteca ou executar seus testes offline, clone-a: git clone https://github.com/Jezue/fitatu_library && cd fitatu_library && pip install -e ".[dev]".

Início rápido

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

Cadastrou-se com Apple ou Facebook? Então a conta não tem senha: copie a sessão do aplicativo web com fitatu-api import-session em vez disso, conforme descrito em Autenticação e arquivo de sessão.

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

Como funcionam gravações, IDs de linha e remoções: docs/PLANNER.md.

Para copiar o menu de uma dieta de catering para o diário (seguro de executar novamente), use catering_sync.py do repositório: Importando um menu de catering.

Para agentes de IA

FitatuTools expõe 47 ferramentas (ler o dia, pesquisar alimentos, registrar alimentos, peso, água, treinos, receitas e produtos privados, ...) com entradas de JSON Schema e anotações somente leitura / destrutivas / idempotentes. call() nunca lança exceções: retorna {"ok": true, "data": ...} ou {"ok": false, "error": {"error_type", "message", "retryable", "hint", ...}}, para que o modelo possa ler o que deu errado. SYSTEM_PROMPT é o prompt de sistema recomendado: as regras que o modelo precisa além das descrições das ferramentas (ler antes de escrever, nomes de refeições dos usuários, data.ok, confirmações). O modelo pergunta a get_today pela data, no fuso horário da conta.

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

As ferramentas de gravação (registrar alimentos, atualizar, mover, copiar e excluir linhas, peso, água, treinos, receitas e produtos privados) precisam de um loop que pergunte ao usuário antes de chamadas destrutivas; está em docs/AGENTS.md, com a tabela de ferramentas, as chamadas que nunca devem ser expostas (tokens de sessão, gravações no catálogo), como tornar as gravações seguras de repetir, o que fazer por error_type, a variante OpenAI, hosts de longa duração e como testar as ferramentas sem uma conta contra o backend de teste offline.

MCP

fitatu-mcp serve as mesmas ferramentas para Claude Code, Claude Desktop e outros hosts MCP via stdio, com SYSTEM_PROMPT como suas instruções e três comandos de barra em polonês (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 serve as mesmas ferramentas via HTTP, com um token, para n8n, outro computador ou Docker (o repositório tem um Dockerfile): docs/AGENTS.md.

Sem uma conta, fitatu-mcp --demo (ou FitatuTools.demo()) serve um pequeno diário fictício mantido em memória. Para Claude Desktop, cada lançamento do GitHub tem fitatu-<version>.mcpb, instalado com um clique duplo (faça login com fitatu-api login primeiro). Sem instalar nada, uvx --from "fitatu-api[mcp]" fitatu-mcp executa o servidor. Ele está listado no Registro MCP como io.github.Jezue/fitatu-api (server.json). Para Hermes Agent e outros hosts que leem Agent Skills, skills/fitatu-diary/SKILL.md diz ao agente como configurar o servidor e usar bem suas ferramentas.

O prompt de permissão do host é a etapa de confirmação, então mantenha-o ativado para as ferramentas destrutivas. Claude Desktop, uvx e o que o servidor faz: docs/AGENTS.md.

O que cobre

ÁreaCliente (FitatuApiClient)Ferramentas de agente
Diário alimentarclient.planner: ler um dia, adicionar produtos, receitas, linhas personalizadas e resultados de pesquisa, atualizar, mover, copiar, remover; a nota do diaget_day, add_food, add_foods, add_custom_item, update_item, move_item, copy_meal, preview_removal, remove_items, set_note
Atividadesplanner.add_activity / remove_activity, client.activities (catálogo, pesquisa, recentes)search_activity, add_activity, remove_activity
Alimentossearch_food, get_product_details, get_recipe, catálogo de receitas e sua pesquisa, client.foods (favoritos, recentes)search_food, search_recipes, get_recipe, find_by_barcode, get_food_measures, get_recent_foods, get_favorite_foods, set_favorite
Alimentos próprios do usuáriosearch_user_food, create_product, update_product, delete_product, create_recipe, update_recipe, delete_recipeget_my_foods; create_food, update_food, delete_food e as ferramentas de receita apenas para alimentos privados do próprio usuário
Medidasclient.measurements: peso e medidas corporais por dia, histórico, gráficosget_weight_history, get_body_measurements, get_measurement_history, log_weight, log_measurements
Águaclient.waterget_water, set_water
Resumo e exportaçãoclient.summary: totais de intervalo, nutrientes por dia, principais produtos, exportação do diárioget_range_summary, get_progress, get_top_foods, get_report, get_shopping_list (calculado a partir das linhas planejadas)
Jejum e hábitosclient.fasting (esquemas, períodos de alimentação por dia), client.habits (catálogo, hábitos do usuário, bons dias); somente leituraget_fasting, get_habits, get_good_days
Metas e configuraçõesclient.user_settings: alvos, BMR/TMR, perfilget_targets, set_targets (a meta manual ou automática, divisão de macronutrientes, tamanho do copo), get_profile (altura, peso, meta, IMC, Premium; nada que identifique o usuário)
Gravações no catálogocreate_recipe(shared=True), set_product_proposal, limpeza de duplicatasnenhuma, de propósito

FitatuLibrary, que envolve a maioria desses como métodos *_via_api que retornam {"status": ...} em vez de lançar exceções, está obsoleto e será removido na versão 1.0: para resultados que nunca lançam exceções, use FitatuTools ({"ok": true, "data": ...}), caso contrário, o cliente. Cada chamada pública com sua estabilidade está listada em docs/API_REFERENCE.md.

Contas gratuitas e Premium

Conta gratuitaPremium
Diário: ler, registrar, atualizar, mover, copiar, remover alimentos, notas, atividades, água, peso, medidas corporais✅✅
Pesquisa, código de barras, medidas de alimentos, pesquisa de receitas por palavras e max_energy_kcal, receitas completas, receitas e produtos privados, favoritos, alimentos recentes✅✅
Filtros de pesquisa de receitas (min_protein_g, max_carbohydrate_g, max_cooking_time, diets, meal) e receitas premium do catálogo (get_recipe, get_food_measures, add_food, add_foods; a lista de compras omite seus ingredientes)❌ "precisa do Fitatu Premium": as ferramentas seguem o aplicativo✅
Metas: ler e definir (get_targets, set_targets), progresso (get_progress), relatórios (get_report), totais de intervalo (get_range_summary)✅ (progresso: uma solicitação por dia, no máximo 31 dias)✅
Histórico de peso e medidas corporais, perfil, jejum e hábitos (leitura), lista de compras✅✅
Principais alimentos (get_top_foods, summary.get_top_products())❌ 403: precisa do Fitatu Premium✅
Exportação do diário (fitatu-api export, summary.export_diary())❌ 403: "precisa do Fitatu Premium"✅
Proteína / gordura / carboidrato dia a dia (summary.get_nutrient_by_day(); energia funciona em ambos)❌ 403 (get_progress contorna isso)✅

Quase tudo funciona em uma conta sem Fitatu Premium. A Fitatu responde 403 sem ele para (verificado ao vivo em 2026-10-15):

  • summary.get_nutrient_by_day() para proteína, gordura e carboidrato (energia funciona em qualquer conta);
  • summary.get_top_products(), e portanto a ferramenta get_top_foods;
  • summary.export_diary(), e portanto fitatu-api export, que então diz isso em uma linha.

A Fitatu serve os filtros de pesquisa de receitas e receitas premium do catálogo para qualquer conta, mas seu aplicativo os mantém para Premium, e as ferramentas também: elas verificam os flags accessControl da conta e respondem com um erro forbidden sem eles. O cliente em si não verifica.

get_progress e get_report funcionam em todas as contas: quando a rota dia a dia de macronutrientes é recusada, eles leem o resumo de cada dia em vez disso (uma solicitação por dia, no máximo 31 dias), e get_report dá top_foods nulo com um top_foods_note. As metas de macronutrientes de uma conta gratuita são um intervalo (target_min abaixo de target_max); em uma conta Premium, um resumo de um dia deu os dois iguais (a rota dia a dia get_progress lida lá concorda, verificado ao vivo). Ele também aceita uma meta manual e sua própria divisão de macronutrientes (set_targets, verificado ao vivo em 2026-10-15); com uma divisão, suas metas de macronutrientes são números únicos. fitatu-api doctor diz se a conta tem um plano premium. Veja Contas gratuitas e Premium em docs/AGENTS.md para as ferramentas.

Linha de comando

fitatu-api (também python -m fitatu_api) funciona no arquivo de sessão, $FITATU_SESSION ou ~/.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 onde vem a sessão (o arquivo e suas permissões, ou $FITATU_SESSION_JSON), o ID do usuário, host da API, localidades, quando o token de portador expira e se há um token de atualização, sem enviar nada; --check também lê o perfil uma vez. doctor verifica a configuração com a Fitatu, uma linha ok / WARN / FAIL cada: as permissões do arquivo de sessão, o token de atualização (ele atualiza o token de portador uma vez e o salva, como qualquer atualização faz), o relógio deste computador contra o tempo de emissão do token novo, uma leitura de perfil, se um plano premium está ativo (não até quando, nem seu preço), e se o aplicativo web da Fitatu ainda está na versão que a biblioteca envia (GET /upgrade-application/WEB/<version>); ele sai com 1 quando uma verificação falha e não imprime token, ID do usuário ou diretório inicial, então sua saída pode ir para um relatório de bug. logout exclui o arquivo de sessão; pare fitatu-mcp e outros processos que o usam primeiro, pois eles escreveriam seus tokens atualizados de volta. A Fitatu não tem nenhuma chamada conhecida pela biblioteca que revogue tokens. Nenhum desses comandos imprime um token. fitatu-api --version imprime a versão.

call executa uma ferramenta via FitatuTools.call() e imprime seu envelope como JSON; o status de saída é 0 quando ok é verdadeiro e 1 quando é falso. Apenas as ferramentas de leitura (READ_ONLY_TOOLS) são executadas a menos que --allow-writes seja fornecido. export salva a exportação do diário da Fitatu (csv, xls, pdf ou txt) em um novo arquivo legível apenas por você (0600); ele não substitui um arquivo existente sem --overwrite, e um intervalo sem nada registrado não escreve arquivo e sai com 1. fitatu-api COMMAND --help descreve cada comando. Os logs vão para stderr, a partir de WARNING, a menos que fitatu-api --log-level DEBUG COMMAND ou FITATU_LOG_LEVEL diga o contrário (veja Privacidade e registro).

Autenticação e arquivo de sessão

fitatu-api login [--email ADDRESS] [-o PATH] [--base-url URL] (também python -m fitatu_api login) faz login, lê seu perfil para as localidades e fuso horário da conta e escreve o arquivo de sessão. Ele lê FITATU_EMAIL e FITATU_PASSWORD quando estão definidos e solicita caso contrário. Contas sem senha usam fitatu-api import-session (abaixo).

  • Onde. fitatu-api login, fitatu-api import-session, FitatuApiClient.from_session(), FitatuTools.from_session() e o obsoleto FitatuLibrary.from_session() usam o mesmo arquivo: o caminho que você passa (-o para a CLI), senão $FITATU_SESSION, senão $FITATU_SESSION_PATH, senão ~/.fitatu/session.json. Um arquivo ausente gera FitatuAuthError informando para você executar fitatu-api login.

  • O quê. JSON com bearer_token, refresh_token, fitatu_user_id, base_url (o host da API da região), app_locale, app_searchlocale, app_storagelocale, app_timezone e uma versão de esquema. A identidade do cliente (chave de API, versão do app, ...) não é armazenada; ela vem da biblioteca.

  • Permissões. O arquivo é gravado somente pelo proprietário (0600), em um diretório criado com 0700, através de um arquivo temporário que o substitui, então um leitor nunca vê meia sessão. Ele contém tokens ativos: trate-o como uma senha.

  • Tokens. Verificados ao vivo em 2026-10-13: o token bearer é um JWT que dura uma hora. O token de atualização é uma string opaca que o Fitatu não rotaciona: POST /token/refresh retorna um novo token bearer e o mesmo token de atualização, que continua funcionando, e tokens bearer mais antigos permanecem válidos até expirarem. POST /login responde a um e-mail ou senha errados com 422, não 401.

  • Atualização e gravação de volta. O cliente atualiza o token bearer 60 segundos antes de ele expirar (do JWT exp) e uma vez após um 401, uma atualização por vez. Os tokens são mesclados no arquivo de sessão, então o próximo processo começa com eles; um processo que compartilha o arquivo pega um par que outro salvou, por exemplo, após fitatu-api login ser executado novamente. Passe persist_tokens=False para manter o arquivo intocado. Um par de outra conta (um login em outra conta com o mesmo arquivo) não é pego: o cliente em execução mantém seus próprios tokens e para de gravar no arquivo, então esse login permanece. Quando um token recém-obtido do Fitatu já parece expirado, o relógio do computador está adiantado: o cliente então registra um aviso e atualiza somente após um 401.

  • Fazendo login novamente. Quando o Fitatu rejeita o token de atualização, as chamadas geram FitatuAuthError (error_type auth). Execute fitatu-api login (ou import-session) novamente; um cliente em execução pega o novo par do arquivo na próxima vez que precisar de uma atualização.

  • Contas sem senha. Contas criadas com Apple ou Facebook (endereços "Ocultar meu e-mail" da Apple incluídos) não têm senha para fitatu-api login. Copie a sessão do aplicativo web em vez disso:

    1. Faça login em https://www.fitatu.com/app/ com Apple ou Facebook.

    2. Abra o console do desenvolvedor do navegador nessa página e execute a linha abaixo. Ela copia apenas os tokens e seu ID de usuário; o localStorage.user do aplicativo web também contém seu e-mail, apelido e mais.

      copy(JSON.stringify((u => ({token: u.token, refresh_token: u.refresh_token, id: u.id}))(JSON.parse(localStorage.user))))
      
    3. No macOS, execute pbpaste | fitatu-api import-session. No Linux ou Windows, cole-o em um arquivo, execute fitatu-api import-session < file (PowerShell: Get-Content file | fitatu-api import-session) e exclua o arquivo.

    import-session [-o PATH] [--base-url URL] lê o JSON da entrada padrão (um arquivo de sessão desta biblioteca também funciona), verifica ambos os tokens com o Fitatu atualizando o bearer uma vez (então o salvo é válido por uma hora), lê os locais e o fuso horário da conta como login e grava o arquivo de sessão da mesma forma. Nada mais da entrada é salvo. O token é uma credencial completa da conta: não o cole em nenhum outro lugar.

  • Contêineres e cron. Onde nenhum arquivo de sessão gravável cabe, coloque o próprio JSON da sessão em FITATU_SESSION_JSON: fitatu-api login --print (ou import-session --print) o imprime na saída padrão em vez de gravar um arquivo, para um armazenamento de segredos. from_session() sem um caminho o pega antes de qualquer arquivo e mantém os tokens que atualiza na memória: o Fitatu não rotaciona o token de atualização, então o mesmo valor funciona para cada execução até que o token de atualização em si expire (quanto tempo ele vive ainda não é conhecido) ou você faça login novamente.

  • Fachada (obsoleta). FitatuLibrary.from_session() carrega o mesmo arquivo e grava tokens atualizados de volta nele. FitatuLibrary(session_data, session_path=...) faz o mesmo para um dict de sessão e copia o par atual em session_data após cada chamada.

  • Do Python. FitatuApiClient.login(email, password, token_store_path=path) faz login e mescla toda a sessão em path.

Regiões e cabeçalhos

O host da API é o base_url da sessão, senão o host do local de armazenamento da conta: pl_PL torna-se https://pl-pl.fitatu.com/api, de_DE torna-se https://de-de.fitatu.com/api. fitatu-api login faz login em https://pl-pl.fitatu.com/api e armazena o host do local de armazenamento; com --base-url ele faz login lá e armazena essa URL.

GrupoCabeçalhosNotas
ObrigatóriosAPI-Key, API-Secret, Authorization: Bearer <jwt>A chave e o segredo são valores estáticos do app (403 sem eles); Authorization é necessário em rotas de usuário (401).
SemânticosAPP-SearchLocale, APP-Locale, APP-StorageLocale, APP-TimezoneAPP-SearchLocale e o parâmetro de consulta locale alteram os resultados de pesquisa. Os outros descrevem o usuário. Todos os quatro vêm da sessão, que login() preenche a partir do perfil; uma sessão sem eles envia os padrões pl_PL / Europe/Warsaw.
IdentificaçãoAPP-OS, APP-Version, APP-UUID, API-Cluster, User-AgentApenas identificam o cliente. A biblioteca sempre envia um perfil constante do aplicativo web (WEB_CLIENT_PROFILE em constants.py), nunca armazenado em sessões.

Se o Fitatu mudar o que o aplicativo web envia (sua versão, ou a chave e o segredo da API) antes de um lançamento da biblioteca seguir, defina FITATU_CLIENT_PROFILE para um objeto JSON com as chaves de WEB_CLIENT_PROFILE para alterar, por exemplo, FITATU_CLIENT_PROFILE='{"app_version": "4.14.0"}'. Qualquer outra chave ou um valor que não seja texto simples é recusado com um FitatuValidationError (fitatu-mcp então não inicia). fitatu-api doctor diz se a versão ainda é a mais nova.

Login (POST /login com _username e _password) e atualização (POST /token/refresh com {"refresh_token": ...}) passam pelo mesmo caminho de solicitação e enviam os mesmos cabeçalhos, sem Authorization; no login, os cabeçalhos de local ainda são os padrões pl_PL. Não há assinatura de solicitação, e o Firebase App Check não é aplicado; a biblioteca não o envia.

Credenciais só vão para hosts https sob fitatu.com: qualquer outro base_url ou URL absoluta gera FitatuValidationError antes que uma solicitação seja feita, e Authorization só é enviado para URLs sob base_url ou https://fitatu.com/api. Redirecionamentos não são seguidos: uma resposta 3xx é um FitatuApiError, então um corpo de solicitação nunca vai para outro host.

Erros, tentativas, limites de taxa

Os erros da biblioteca são subclasses de FitatuApiError. describe_error(exc) transforma qualquer exceção no dict seguro para JSON que FitatuTools.call() (e o obsoleto FitatuLibrary) retornam.

ExceçãoGerada quandoerror_type
FitatuValidationError (também um ValueError)Um argumento é inválido; nada foi enviado.validation
FitatuNotFoundErrorO id, refeição ou linha que você nomeou não está lá: a biblioteca encontrou isso no que leu (a mensagem lista o que está lá), ou o servidor respondeu 404 (status_code 404).not_found
FitatuAuthError401 após uma atualização, um token de atualização rejeitado, nenhum arquivo de sessão, ou um e-mail ou senha errados no login (o Fitatu responde 422); hint então aponta para import-session.auth
FitatuRateLimitError429 após tentativas; retry_after contém a espera do servidor em segundos.rate_limit
FitatuConnectionErrorNenhuma resposta HTTP (DNS, timeout, conexão redefinida).network
FitatuMaintenanceErrorO cabeçalho technical-break do Fitatu; display_from / display_to.maintenance
FitatuPlannerSyncErrorA sincronização do dia respondeu 202 mas rejeitou um dia; results tem os erros por dia.sync_rejected
FitatuPremiumRequiredErrorUma ferramenta para o que o app Fitatu mantém para Premium, em uma conta sem ele; nada foi enviado para isso.forbidden
FitatuApiErrorQualquer outro erro HTTP: status_code, body, codename (por exemplo, NUMBER_OF_USES_EXCEEDED).forbidden (403), validation (400/409/422), server

Mensagens leem METHOD /path -> status CODENAME: server message. Na mensagem de describe_error(), o ID do usuário é <user> (GET /diet-plan/<user>/days -> 403), e com user_id= (que FitatuTools.call() passa) onde quer que apareça, então um resultado não o carrega para o provedor de modelo. Um 403 cujo corpo é uma página HTML vem de um firewall na frente da API (por exemplo, Cloudflare), não das verificações de premium ou chave do Fitatu, e seu hint diz isso. Qualquer outra exceção, como um TypeError gerado dentro da biblioteca, é unexpected: um bug para relatar, já que FitatuTools.call() verifica os argumentos primeiro.

Tentativas: erros de rede, 429, 500/502/503/504 e 520 a 524 do Cloudflare são tentados novamente por GET, HEAD e OPTIONS, e para gravações marcadas como idempotentes: a sincronização do dia (todas as gravações do planejador; ela faz upsert por ID de linha), water.set_day, measurements.set_day, measurements.delete_day e foods.set_favorite. Outras gravações (gravações de catálogo, configurações, login, atualização) são enviadas uma vez. Até retry_max_attempts=3 tentativas com backoff exponencial de retry_base_delay_seconds=0.5. Um Retry-After de até 30 segundos é respeitado; um mais longo não é esperado, e o erro é gerado imediatamente (FitatuRateLimitError com retry_after para um 429). Durante uma pausa técnica, nada é tentado novamente ou atualizado. Dentro de with client.time_limit(seconds):, as solicitações terminam dentro desse tempo no total: uma tentativa cuja espera deixaria menos de 2 segundos não é feita, o timeout de cada solicitação é reduzido para o tempo restante, e uma solicitação sem tempo restante não é enviada (FitatuConnectionError). FitatuTools executa cada chamada em um de 45 segundos (call_timeout).

A maioria das gravações faz várias solicitações, o que conta quando você faz orçamento contra 429s: as ferramentas leem o dia antes de cada gravação de diário, e atualizações, movimentos, remoções e gravações de atividade o leem novamente depois para definir ok. Solicitações por chamada de ferramenta do agente, antes de tentativas e atualizações de token:

SolicitaçõesFerramentas
0get_today
1as outras ferramentas de leitura; set_water; set_favorite; preview_removal e remove_items com dry_run; delete_recipe de uma receita já excluída, delete_food de um produto já excluído
2get_range_summary, get_profile, get_fasting (3 quando as configurações nomeiam o esquema por id), get_habits, find_by_barcode (1 sem um resultado de produto), add_food para uma receita em porções (mais 1 para os sinalizadores Premium, a primeira chamada desse tipo em 10 minutos; sem Fitatu Premium, mais 1, pois lê a receita primeiro), add_custom_item, log_weight, delete_recipe, set_targets, create_food, delete_food; add_foods, mais 1 para cada item com unit ou measure_id e, sem Fitatu Premium, para cada receita em porções (e 1 para os sinalizadores Premium como em add_food); create_recipe, mais 1 para cada ingrediente; search_recipes com um filtro, mas max_energy_kcal, e get_recipe e get_food_measures de uma receita premium, a primeira chamada desse tipo em um FitatuTools em 10 minutos (lê os sinalizadores Premium da conta; 1 depois)
3add_food com unit ou measure_id (lê o alimento primeiro), log_measurements (lê a unidade de medida do perfil), copy_meal dentro de um dia, remove_items, add_activity, remove_activity, set_note, update_food; update_recipe, mais 1 para cada novo ingrediente
4update_item, move_item dentro de um dia, copy_meal para outro dia
5get_progress (qualquer intervalo de até 62 dias; sem Fitatu Premium, veja abaixo)
6move_item para outro dia
8get_report (mais um para cada 41 pesagens adicionais no intervalo)
1 por diaget_shopping_list, mais 1 para cada receita planejada no intervalo (e 1 para os sinalizadores Premium quando uma é premium); get_progress e get_report sem Fitatu Premium, mais 1 para o intervalo (nenhum para um único dia) e as pesagens de get_report, e a primeira chamada desse tipo em um FitatuTools em 10 minutos, mais 1, que o Fitatu recusa

Privacidade e registro

A biblioteca registra por meio do módulo padrão logging e não configura manipuladores. O comando fitatu-api e fitatu-mcp registram em stderr, nunca em stdout (que carrega o resultado do comando, ou o protocolo MCP): a biblioteca a partir de WARNING, ou do nível 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 define; outras bibliotecas a partir de WARNING.

  • INFO (fitatu_api.client): eventos de autenticação como linhas JSON: login (id do usuário), início de atualização de token, sucesso e falha, e clear_auth, com um instantâneo de estado não secreto (id do usuário, locais, fuso horário, host, expiração do token, se os tokens estão presentes).
  • WARNING (fitatu_api.client): fitatu.request.error para uma solicitação que falhou definitivamente em um erro de rede, um 429 ou um 5xx, após suas tentativas; fitatu.auth.clock_skew (um token fresco do Fitatu parece expirado, então o relógio local está errado; atualizar antes da expiração para) e fitatu.auth.store_other_account (o arquivo de sessão agora contém os tokens de outra conta; o cliente mantém os seus próprios). De fitatu_api.agent, fitatu_api.facade e fitatu_api.mcp_server, uma chamada cujo resultado relata um erro unexpected (com o traceback), um auth ou um erro maintenance: get_day failed (auth): .... De fitatu_api.auth, um armazenamento de tokens (geralmente o arquivo de sessão) que não pôde ser lido. De fitatu_api.mcp_server sob --http, cada solicitação recusada por seu token, com o endereço do cliente (nunca o token); o uvicorn adiciona suas linhas de início e parada.
  • DEBUG (fitatu_api.client): cada solicitação como linhas JSON: fitatu.request.start (método, caminho, parâmetros de consulta, como frases de pesquisa), fitatu.request.retry antes da espera de cada tentativa (o attempt falhou, delay_seconds, e o reason: o status ou o tipo de erro de rede), então fitatu.request.ok ou fitatu.request.error (status, attempt, elapsed_ms desde o início, e os primeiros 300 caracteres de um corpo de erro; um 4xx permanece em DEBUG, pois o chamador o recebe como uma exceção e alguns, como um 404, são esperados). De fitatu_api.planner, uma linha por gravação do planejador (id do usuário, dia, refeição, id do alimento, o nome e kcal de uma linha personalizada); das camadas de ferramentas, todos os outros erros que um resultado relata. Caminhos contêm o id do usuário e datas.

Corpos de solicitação, senhas e tokens nunca são registrados: valores sob chaves contendo token, secret, password ou authorization são substituídos por [redacted], e o contexto de autenticação mantém tokens e o segredo da API fora de seu repr. Mensagens de erro carregam até 200 caracteres da mensagem do servidor; o corpo completo da resposta está em exc.body, que describe_error() e os resultados de erro de FitatuTools (e o obsoleto FitatuLibrary) deixam de fora. Um token que não é um valor de cabeçalho válido (por exemplo, colado com uma nova linha) é relatado sem aspas.

Testes

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

A suíte roda offline. tests/fixtures/api_recording.json contém respostas reais da API (limpas de dados pessoais) para a sincronização do dia, medições, resumos, pesquisa e configurações; tests/fake_server.py é um backend falso construído a partir delas, e tests/test_recorded_contract.py falha se o falso se desviar da gravação. Como a gravação é feita e atualizada: tests/fixtures/README.md.

A suíte ao vivo (tests/test_live_all.py) roda contra uma conta real e escreve nela:

FITATU_RUN_LIVE_TESTS=1 pytest tests/test_live_all.py

Ela usa o arquivo de sessão que fitatu-api login escreveu (ou $FITATU_SESSION) e salva tokens atualizados de volta nele; com $FITATU_SESSION_JSON definido, essa sessão é usada em vez disso e tokens atualizados não são salvos. Opcional: FITATU_LIVE_DAY (após hoje; padrão um ano a partir de hoje), FITATU_LIVE_MEAL_KEY (snack), FITATU_LIVE_PRODUCT_ID, FITATU_LIVE_RECIPE_ID, FITATU_LIVE_MEASUREMENT_DAY (2020-01-01; ignorado se tiver uma medição) e FITATU_LIVE_RUN_WRITE_ACCOUNT=1.

Cada teste exclui exatamente as linhas que adicionou em FITATU_LIVE_DAY e falha se uma sobrar; um dia em ou antes de hoje é recusado. Água e a nota naquele dia são restauradas, e o peso e medições escritas em FITATU_LIVE_MEASUREMENT_DAY são excluídos. -k ReadOnly roda apenas os testes que leem. Registrar alimentos do catálogo os adiciona aos alimentos recentes da conta, o que não é desfeito. Com FITATU_LIVE_RUN_WRITE_ACCOUNT=1 a suíte também altera e restaura o tamanho do copo de água e as metas diárias (o Fitatu mantém as metas restauradas como uma nova versão a partir de hoje), e cria, depois exclui, um produto privado e duas receitas privadas (uma pelas ferramentas, registrada em FITATU_LIVE_DAY e removida) e um produto privado pelas ferramentas (registrado em FITATU_LIVE_DAY, alterado, removido). Exclusões de produto e receita são suaves, então como os alimentos recentes não são totalmente desfeitas: elas permanecem na conta, marcadas como excluídas e ocultas dos seus alimentos e pesquisas. A lista completa está no docstring do módulo de tests/test_live_all.py. Nunca aponte FITATU_LIVE_DAY para um dia com entradas que você se importa.

scripts/check_contract.py informa, apenas lendo, se a API ao vivo ainda responde com a estrutura que a gravação contém: imprime nomes de rotas e chaves, nunca valores, e sai com 1 quando uma chave sumiu, um tipo mudou ou uma rota falha. O fluxo de trabalho Live contract o executa e os testes ao vivo somente leitura toda segunda-feira com a sessão de uma conta descartável no segredo FITATU_SESSION_JSON, e pula sem ele: CONTRIBUTING.md.

scripts/eval_agent.py pontua quão bem um modelo Claude usa as ferramentas no diário de demonstração (29 tarefas em polonês e inglês; precisa de ANTHROPIC_API_KEY e custa uso de API, então CI não o executa): docs/AGENTS.md.

Documentação

Bugs e perguntas: problemas do GitHub. Problemas de segurança são relatados em particular: SECURITY.md. Configuração, verificações e regras para pull requests: CONTRIBUTING.md.

Licença

MIT, veja LICENSE. Cobre este código apenas, não o serviço Fitatu, sua API ou os dados que ele retorna.