mcpField
B
bodylog

bodylog

本地服务
github.com/abhaymettu健康医疗05 周前更新

关于 bodylog

bodylog 是供 Agent 使用的个人训练与饮食记录工具,包含命令行工具、Python 库和 Claude 技能。数据保存在本地 SQLite 文件中,营养数据来自 USDA 和 Open Food Facts,无需账户或云存储。

主要功能

  1. 01通过聊天记录训练和饮食
  2. 02追踪热量、宏量营养素、目标与连续记录
  3. 03使用 USDA 和 Open Food Facts 数据
  4. 04将记录存储在本地 SQLite 文件
  5. 05查看训练历史和个人纪录
  6. 06生成可分享的训练与饮食卡片

适用场景

  1. 01训练时记录组数和重量
  2. 02记录餐食并查看每日营养总量
  3. 03查看每周训练量和个人纪录
  4. 04制作可分享的训练或饮食日卡片

MCP 相关

bodylog 内含 MCP 服务,可通过工具记录和修改训练与饮食、查看进度及生成卡片,并可与任何 MCP 客户端配合使用。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"abhaymettu-bodylog": {
"command": "uvx",
"args": [
"bodylog"
],
"env": {
"BODYLOG_DB": "<BODYLOG_DB>",
"BODYLOG_CARDS": "<BODYLOG_CARDS>",
"FDC_API_KEY": "<FDC_API_KEY>",
"BODYLOG_OFFLINE": "<BODYLOG_OFFLINE>"
}
}
}
}
启动命令uvx bodylog
需要配置的环境变量
BODYLOG_DBSQLite file for the log (default ~/.bodylog/log.db)
BODYLOG_CARDSDirectory for rendered card PNGs (default ~/.bodylog/cards)
FDC_API_KEY密钥USDA FoodData Central API key (free at api.data.gov); DEMO_KEY is used without one
BODYLOG_OFFLINESet to 1 to use only the bundled USDA table, no network lookups

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

安装包
pypibodylog0.1.0

工具

15

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

参数类型必填说明
rpeany否
kindstring否
repsany否
textany否
unitany否
countinteger否
notesany否
weightany否
exerciseany否

详细介绍

bodylog

面向个人 Agent 的训练与饮食日志。你可以像平时口头描述那样,在聊天中告诉 Agent 你的训练组数和饮食。它会记录下来,根据真实营养数据计算卡路里和宏量营养素、帮你保持连续记录,并生成可分享的卡片。

一个软件包,适用于任何 Agent:MCP 服务(bodylog-mcp)、命令行工具(bodylog)、Python 库和 Claude 技能。所有数据都保存在一个本地 SQLite 文件中。无需账户、云服务或社交功能。

饮食日故事训练
饮食日卡片饮食故事卡片训练卡片

更多:饮食,浅色、同一天的饮食和训练 (完整图)、训练故事、 8 项训练动作,分两页、混合使用 kg 和 lb、 照片贴纸。

安装

Python 3.10+。

pip install bodylog          # CLI + library + MCP server
uvx --from bodylog bodylog-mcp   # run the MCP server without installing

Claude Code 插件版(技能和 MCP 服务合二为一):

/plugin marketplace add abhaymettu/bodylog
/plugin install bodylog@bodylog

Claude Code,仅安装 MCP 服务:

claude mcp add bodylog -- uvx --from bodylog bodylog-mcp

其他 MCP 客户端:

{
  "mcpServers": {
    "bodylog": {
      "command": "uvx",
      "args": ["--from", "bodylog", "bodylog-mcp"],
      "env": { "FDC_API_KEY": "optional, free at https://api.data.gov/signup/" }
    }
  }
}

设置项,均为可选:

变量默认值说明
BODYLOG_DB~/.bodylog/log.dbSQLite 文件路径
BODYLOG_CARDS~/.bodylog/cards/卡片 PNG 文件的保存位置
BODYLOG_UNIT记录最多的单位训练总量使用 kg 或 lb
FDC_API_KEYDEMO_KEYUSDA FoodData Central 密钥;DEMO_KEY 每小时仅允许查询少数几次
BODYLOG_OFFLINE关闭1 表示仅使用内置食物表,不访问网络

技能文件位于 skills/bodylog/SKILL.md。如果不使用插件,请将 skills/bodylog/ 复制或符号链接到 Agent 的技能文件夹中。

饮食

CLI 会打印 MCP 工具返回的内容(bodylog eat 对应 log_food)。一次真实的离线运行:

$ bodylog eat "2 eggs, toast and a protein shake"
#1 breakfast: 2 × egg, 100 g: 143 kcal, P 12.6 C 0.7 F 9.5
#2 breakfast: toast, 22 g: 64 kcal, P 2 C 12 F 0.9
#3 breakfast: whey protein, 26 g: 92 kcal, P 20.3 C 1.6 F 0.4
$ bodylog eat "greek yogurt with blueberries and a handful of almonds"
#4 breakfast: greek yogurt, 170 g: 100 kcal, P 17.3 C 6.1 F 0.7
#5 breakfast: blueberries, 148 g: 84 kcal, P 1.1 C 21.4 F 0.5
#6 breakfast: 1 handful almonds: not counted (no weight for a handful of almonds; give grams or one of: almond, cup, oz)
$ bodylog food-fix 6 --grams 28
#6 breakfast: 28 g almonds: 162 kcal, P 5.9 C 6 F 14

只给出数量时,会使用 USDA 针对该食物的份量:一个大鸡蛋(50 g)、一片吐司、一勺乳清蛋白粉 (FNDDS 中的“1 scoop, NFS”,26 g)。通过 MCP,Agent 会针对第 6 项提出一个简短问题,而不是猜测。

食物营养数据的来源:

  1. 内置表格。 来自 USDA FoodData Central(SR Legacy 和 FNDDS)的 62 种常见食物,每种都包含 FDC id、每 100 g 的数值以及 USDA 自带的份量重量(一个大鸡蛋、一片面包、一杯米饭)。可离线使用。 由 scripts/build_common_foods.py 根据 USDA 下载的数据重新构建。
  2. 缓存。 之前查询过的内容,存储在 SQLite 文件中。
  3. USDA FoodData Central 搜索(POST /fdc/v1/foods/search,survey、SR Legacy 和 Foundation 食物),使用 FDC_API_KEY 或 DEMO_KEY。
  4. Open Food Facts 搜索品牌和包装食品,并通过条形码查询产品 (GET /api/v2/product/<barcode>.json,无需密钥)。

只有当搜索结果名称包含你说出的每个词时,才会使用该结果。否则,该项目会保留为 unknown,并附上最接近的候选项,以便 Agent 询问你指的是哪一个。若某项的数量无法换算成克(例如一把,或没有 USDA 体积重量数据的食物的毫升数),则会保留为 needs_amount。修正之前,这两类项目都不会计入总量,也绝不会填入编造的数值。你从标签上读取的数值会按原样存储(manual)。

此外还包括:每日 kcal、蛋白质、碳水化合物和脂肪目标;以天为单位的记录连续天数(尚未记录的当天不会中断连续记录)和以周为单位的训练连续周数;以及深色、浅色、清晰或故事格式的每日卡片。当天还进行了训练时,卡片会添加一个训练区块。

训练

30 秒演示

下面的对话来自一次真实运行,调用方式为 log_set(text=...)。右栏显示了存储中为每条消息记录的内容。

you:   push day                                  -> title: Push Day
you:   bench 20 kg x 10 warm up                  -> Bench Press: 20 kg x 10 (warmup)
you:   60kg x 10                                 -> Bench Press: 60 kg x 10
you:   67.5 kg x 4                               -> Bench Press: 67.5 kg x 4
you:   62.5 kg x 8 rpe 9                         -> Bench Press: 62.5 kg x 8
you:   next incline db press 55 lb x 10          -> Incline Dumbbell Press: 55 lb x 10
you:   same                                      -> Incline Dumbbell Press: 55 lb x 10
you:   55lb x 9                                  -> Incline Dumbbell Press: 55 lb x 9
you:   12.5 kg x 12                              -> Incline Dumbbell Press: 12.5 kg x 12
you:   that was triceps pushdowns on the cable   -> named 1 set(s) Triceps Cable Pushdown
you:   12.5 kg x 11                              -> Triceps Cable Pushdown: 12.5 kg x 11
you:   lateral raise 15 lb x 14                  -> Lateral Raise: 15 lb x 14
you:   15 lb x 12                                -> Lateral Raise: 15 lb x 12
you:   one more                                  -> Lateral Raise: 15 lb x 12
you:   wrapped up, workout took 1h 3m            -> ended session 4
**Push Day**
Thursday, Sep 17 · 6:05 PM
1h 3m · 2,640 kg · 11 sets · 12 PRs
Muscles: Chest 55%, Shoulders 27%, Triceps 18%

**Bench Press** · Chest · 3 sets · best 60 kg x 10 · PR: Heaviest weight, Best 1RM, Best set volume, Most reps
  W. 20 kg x 10 (warm-up)
  1. 60 kg x 10 🏆 Best 1RM, Best set volume, Most reps
  2. 67.5 kg x 4 🏆 Heaviest weight
  3. 62.5 kg x 8 @ RPE 9
**Incline Dumbbell Press** · Chest · 3 sets · best 55 lb x 10 · PR: Best 1RM, Best set volume, Most reps
  1. 55 lb x 10 🏆 Best 1RM, Best set volume, Most reps
  2. 55 lb x 10
  3. 55 lb x 9
**Triceps Cable Pushdown** · Triceps · 2 sets · best 12.5 kg x 12 · PR: Heaviest weight, Best 1RM
  1. 12.5 kg x 12 🏆 Heaviest weight, Best 1RM
  2. 12.5 kg x 11
**Lateral Raise** · Shoulders · 3 sets · best 15 lb x 14 · PR: Best 1RM, Best set volume, Most reps
  1. 15 lb x 14 🏆 Best 1RM, Best set volume, Most reps
  2. 15 lb x 12
  3. 15 lb x 12

This week: 2 workouts, 4,687 kg (−14% vs last week to date)

最后一条消息会结束训练,因此工具会返回这张卡片以及本页顶部的训练 PNG(end_session 也会执行相同操作)。个人纪录(PR)会根据 tests/fixtures/chat.txt 中此前的三次训练记录进行统计。

它如何解析聊天内容

  • 组数:60kg x 8、60 x 8、50lbs for 9、8 reps at 60kg、3x10 @ 90lb、60kg 3x8、12 reps (自重)。标记:warmup、drop set、to failure、rpe 9。
  • 训练动作:在消息开头命名(incline db press 55 lb x 10)、提前说明(now squats), 或事后命名(this is tricep cable pushdowns btw)。未命名的组会沿用上一个训练动作。
  • 事后命名会应用到尚未指定训练动作的组。如果每组都已指定动作,则会应用到最后一组重量相同的连续组。 这涵盖了通常的情况:你换到新器械后,过一两组才说出动作名称。如果你在命名之前换了重量,请对较早的组使用 edit_last_set(exercise=...)。
  • 名称会统一归为同一个训练动作:忽略大小写、复数形式、db/bb 简写、词序、填充词(on the) 以及不改变动作本身的肌肉名称。因此,tricep cable pushdowns、cable pushdown 和 triceps pushdowns on the cable 会被视为同一个动作,而 triceps curl 和 biceps curl 则会区分开。 bodylog alias 可用于添加其他名称。
  • 命令:same / one more 重复上一组;done, 1h 5m / workout took 45 min 结束训练。粘贴记录中 Agent 的回复(Agent:、Assistant:、Claude:)会被跳过。

数字的含义

  • 训练量是工作组的重量 x 次数。热身组会列在卡片上,但不计入训练量、组数和 PR。每组都会显示记录时使用的单位。总量会将每组精确换算为一个单位:你传入的 unit,否则使用 $BODYLOG_UNIT,再否则使用组数最多的单位。
  • 肌群分布是各主要肌群所占工作组的比例,依据 muscles.py 中涵盖常见举重动作的名称映射(未知名称计为“其他”)。百分比总和始终为 100。
  • 估算 1RM 使用 Epley 公式:weight x (1 + reps / 30)。
  • PR 以更早开始的训练为基准统计:最重重量、最佳估算 1RM、最佳单组训练量(单组中的重量 x 次数),以及在你之前练过的某个重量下完成的最多次数(每个动作只记一项,取增幅最大者)。首次进行某个动作时不会产生 PR,因此首次训练不会每组都显示 PR。
  • 周度数据行会将本周与上周截至相同星期和时间的数据进行比较,因此周一训练不会显示为下降 90%。
  • 时长以你结束时提供的数值为准。训练开始时间设为结束时间减去该时长。

MCP 工具

工具功能
log_set向正在进行的训练中添加一组(如果没有,则开启一场训练)。可提供结构化字段或原始消息作为 text。返回该组打破的任何 PR。
edit_last_set修正或删除最后一组。
end_session以用户提供的时长(“1h 5m”)结束训练。以文本和 PNG 页面形式返回卡片。
session_card任意一场训练的卡片:theme 可选 dark、light 或 clear;style 可选 full 或 story;unit 可选 kg 或 lb。
exercise_history、prs、weekly_volume分别查看每个动作的进展、历史记录和每周训练量。
import_chat从粘贴的聊天记录中补录训练。可安全重复执行。
log_food根据用户描述记录食物;也可按名称、条形码或食物 ID 记录单项食物并指定数量,或提供标签上的数值。返回每项食物的状态和当日总量。
edit_food修正某项食物的数量、食物、餐次或数值;也可删除该项。
lookup_food按名称或条形码搜索,不记录食物。
food_day查看某一天按餐次分类的食物项目、总量、目标、剩余量、连续记录、标记项目和训练。
food_card以文本和 PNG 形式返回每日卡片:theme、style 可选 full 或 story。
set_goals、streaks每日目标;记录和训练连续天数。

CLI 和库

bodylog log "bench 60kg x 8"
bodylog end "1h 5m" --title "Push Day"
bodylog eat "chicken breast 180g, 1.5 cups rice and broccoli"
bodylog food-fix 12 --grams 28
bodylog goals --kcal 2400 --protein 160
bodylog food-card --style story
bodylog --help
from bodylog import Store, log_text, log_food, day_summary, summary, render_png
from bodylog.foodcard import render_png as food_png

store = Store()                                  # ~/.bodylog/log.db
log_text(store, "bench 60kg x 8")
log_food(store, "2 eggs, toast and a protein shake")
food_png(day_summary(store), "today.png", style="story")

为什么选择 Python

MCP Python SDK 为每个工具只需几行代码,Pillow 无需浏览器即可绘制卡片,sqlite3 和 urllib 则随 Python 一同提供。运行时依赖项为 Pillow 和 mcp。卡片使用 Inter 字体(SIL Open Font License,随附于 src/bodylog/fonts/),因此在任何环境中的显示效果都一致。

开发

uv sync
uv run pytest                                            # offline: API calls replay recorded responses
uv run python scripts/render_examples.py   # regenerate examples/ (offline)

测试绝不会访问网络或仓库:tests/fixtures/http/ 中保存了真实的 USDA 和 Open Food Facts 响应,测试写入的所有文件都会放到临时目录中。

目录结构:store.py(SQLite)、names.py、chatlog.py、stats.py、muscles.py、card.py(训练)、food.py(餐食解析、匹配、总量、连续记录)、sources.py(USDA 和 Open Food Facts)、foodcard.py(每日卡片)、cli.py、server.py(MCP)、data/common_foods.json(内置 USDA 表)。

致谢

食物数据来自:USDA FoodData Central(公有领域)和 Open Food Facts(Open Database License;产品数据 注明来源为 Open Food Facts 贡献者)。

评论

支持 Markdown

第一次接触 MCP?

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

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