fatsecret-mcp
fatsecret-mcp
一个个人远程 MCP(Model Context Protocol)服务器,让 Claude 可以直接在对话中搜索 FatSecret 的食物/食谱数据库,并读写你自己的食物日记、体重和运动日志。部署在 Vercel 的免费 Hobby 套餐上。是 fitness-mcp(Hevy)的姊妹项目——每个产品一个 MCP 服务器,共享相同的认证模式。
License
Related MCP server: Nutrition MCP
Status
Search (Phase 2):已实现 —
search_foods、get_food_detail、search_recipes、get_recipe_detail、find_food_by_barcode。无需 FatSecret 用户授权;只需要 FatSecret 开发者控制台中的 OAuth 2.0 Client ID/Secret。Diary/weight/exercise/profile (Phase 4):已实现,并且已针对真实 FatSecret 账户部分验证 —
get_profile、get_food_diary和get_exercise_diary现已确认可用;create_exercise_entry、weight.update和find_food_by_barcode仍是未经验证的尽力重建(完整细分见下文“未验证内容”)。3-legged OAuth1 setup script (Phase 3):已实现(
scripts/fatsecret-oauth-setup.ts),尚未针对真实 FatSecret 账户运行。
Two authentication layers
此服务器位于 Claude 和 FatSecret 之间,这两段关系的认证方式完全不同——这是接触代码前需要理解的主要事项。
Claude <──①── this server (fatsecret-mcp) ──②──> FatSecret API① Claude ↔ 此服务器 — 使用单一共享密钥,与 fitness-mcp 的模式相同。Claude 在每个请求中发送 Authorization: Bearer <MCP_BEARER_TOKEN>;lib/auth.ts 负责检查。由于 Claude 的静态请求头选项仍处于 beta 限制阶段,此服务器还运行自己的最小 OAuth 2.1 授权服务器(lib/oauth.ts、/api/oauth/authorize、/api/oauth/token),以便 Claude 的标准 OAuth Client ID/Secret 字段作为始终可用的后备方案——完整理由见 fitness-mcp 的 README,此处同样适用。
此层上的每次失败——MCP_BEARER_TOKEN 错误或缺失、无法识别的 OAuth client_id、错误的 client_secret、无效的 PKCE、不允许的 redirect_uri——都会被记录,并可选地实时告警;见下文“安全事件记录与告警”。
② 此服务器 ↔ FatSecret — 这里比 fitness-mcp 更复杂,因为 FatSecret 本身对两类 API 方法使用两种不同的 OAuth 版本,这是无法回避的——这是 FatSecret API 的设计方式,而非此处做出的选择:
FatSecret method category | Example methods | How this server authenticates |
签名请求(不涉及特定用户) |
| OAuth 2.0 客户端凭证 — |
签名且委托的请求(读取/写入你的 FatSecret 账户) |
| OAuth 1.0a,三方,HMAC-SHA1 签名 — |
具体来说:一旦你注册了 FatSecret 应用并设置了 FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET,search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 即可工作。其他所有工具还需要 FATSECRET_CONSUMER_KEY/FATSECRET_CONSUMER_SECRET(OAuth1 — 来自同一 FatSecret 应用的不同凭证对)和 FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET(通过运行一次设置脚本获得)。
Security event logging & alerting
上述第①层(Claude ↔ 此服务器)上的每次失败检查都会通过 lib/securityAlert.ts 报告,涉及以下位置:
lib/auth.ts(verifyBearerToken)— 缺少 bearer token、bearer token 错误、MCP_BEARER_TOKEN未配置。/api/oauth/authorize— 无法识别的client_id、不允许的redirect_uri(isAllowedRedirectUri用于阻止开放重定向情况)、不支持的response_type、缺失/非 S256 的 PKCE challenge、OAUTH_CLIENT_SECRET未配置。/api/oauth/token— 错误的client_secret、无效/过期的授权码、code/PKCE/redirect_uri 不匹配、MCP_BEARER_TOKEN未配置。
两个独立层,因此可以优雅降级:
始终记录。 上述每次失败都会通过
console.error向stderr写入一行结构化 JSON(event、reason、ip、userAgent、path、time)——无需设置,在 Vercel 上会原样显示在部署的函数日志中。实际的 bearer token / client secret / PKCE verifier 值永远不会包含——只包含失败尝试的元数据——因为一个可能泄露其监视的密钥的检测机制会适得其反;lib/securityAlert.test.ts和lib/auth.test.ts直接断言了这一点。可选实时告警。 如果设置了
SECURITY_ALERT_WEBHOOK_URL(Slack 或 Discord 的“incoming webhook” URL),同一事件也会以单行消息 POST 到该地址,因此入侵尝试会以推送通知的形式出现,而不是只有有人碰巧打开 Vercel 日志查看器时才可见。webhook 投递失败(URL 过期、网络错误)本身会记录为security_alert_delivery_failed,因此静默损坏的 webhook 不会被视为“没有尝试”。
webhook POST 通过 Next 的 after() 调度,因此在响应已发送后运行(不会增加认证检查的延迟);这仅在真实请求内有效,因此当直接调用时(例如从测试中)会回退为简单的即发即弃调用。
这有意采用简单的“每次失败都告警”设计,而非基于阈值/速率的告警——有关被排除的内容(基于计数的阈值、Vercel 自身的平台级监控、凭证轮换)及其原因,请参阅 lib/auth.ts/lib/securityAlert.ts 的文档注释。
Tools exposed
Tool | Type | Auth needed | Description |
| read | OAuth2 (app) | 按名称搜索 FatSecret 的食物数据库 |
| read | OAuth2 (app) | 一种食物的完整每份营养信息 |
| read | OAuth2 (app) | 搜索 FatSecret 的食谱数据库 |
| read | OAuth2 (app) | 一种食谱的完整配料/做法 |
| read | OAuth2 (app) | 将 GTIN-13 条形码解析为 foodId — 需要 |
| read | OAuth1 (user) | 列出某日期的食物日记条目 |
| read | OAuth1 (user) | 列出收藏的食物 |
| read | OAuth1 (user) | 列出最常吃的食物,可选按餐次 |
| read | OAuth1 (user) | 列出最近吃过的食物,可选按餐次 |
| read | OAuth1 (user) | 列出某月的体重条目 — 可能仅限 Premier |
| read | OAuth1 (user) | 列出某日期的运动条目 |
| read | OAuth1 (user) | 获取用户的 FatSecret 个人资料摘要 |
| write | OAuth1 (user) | 将食物记录到日记 |
| write | OAuth1 (user) | 更新现有日记条目 |
| write | OAuth1 (user) | 删除日记条目 |
| write | OAuth1 (user) | 记录/更新体重条目 — 可能仅限 Premier |
| write | OAuth1 (user) | 记录运动条目 |
Write tools are dry-run by default
与 fitness-mcp 相同的设计:每个写入工具都需要 confirm: true 参数。其描述指示调用 LLM 先向用户准确展示将要写入的内容并获得明确同意。这是一种结构性提示,而非保证——决定是否调用工具的同一个 LLM 也设置 confirm,并且在认证层没有读写工具之间的作用域分离,因此任何持有有效 MCP_BEARER_TOKEN 的调用者都可以调用任何工具。
What's unverified
此项目最初构建时没有 FatSecret API 注册,因此大部分内容是从尽力重建开始的。此后已针对真实账户检查了部分工具——状态如下:
已确认可用,与实现完全一致:
search_foods(foods.search)、get_food_diary(food_entries.get,包括meal字段的真实大小写,例如"Breakfast")。已确认可用,核对后已修复:
get_profile(profile.get)——真实响应中包含height_cm,但之前尚未作为字段暴露;现已添加。已确认可用,真实结构比预想更复杂:
get_exercise_diary(exercise_entries.get)。方法/信封结构是真实的,但从已连接的健康应用同步的真实条目({exercise_id: "184", exercise_name: "Google Health Connect", minutes: "1440", calories: "1655"}——一整天的汇总活动,而非单次锻炼)完全没有exercise_entry_id,也没有date_int。lib/fatsecret/exercise.ts现在做了防御性处理(缺失字段变为null,不会崩溃也不会生成误导性的伪造值),并在raw下保留完整原始条目。仍待确认:通过 FatSecret 应用手动记录的锻炼是否像food_entries.get的条目那样带有 id/date——尚未测试。仍未验证 / 尽力而为的重构:
food.find_id_for_barcode的响应结构、weight.update的参数名,以及create_exercise_entry的方法名和参数(上述锻炼日记的发现意味着其整个"可单独创建的条目"数据模型假设可能不成立——参见lib/fatsecret/exercise.ts中的警告)。请将这些视为起点,而非已验证的事实。针对上述两条中的任何内容,请对照真实账户运行下方的手动验证清单,并修复发现的任何不匹配(
lib/fatsecret/*.test.ts中的单元测试需要同步更新)。
设置
在 https://platform.fatsecret.com/ 注册一个 FatSecret Platform API 应用。你将获得:
OAuth 2.0 Client ID/Secret(用于
FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET)。OAuth 1.0 Consumer Key/Secret(用于
FATSECRET_CONSUMER_KEY/FATSECRET_CONSUMER_SECRET)——来自同一应用的另一对凭据,与上述 OAuth2 凭据不同。检查你的套餐包含哪些 scope(
basic/premier/barcode/ ...)——weights.get_month/weight.update/find_food_by_barcode据称需要 Premier 或barcode/premierscope;请对照自己的套餐确认,必要时调整FATSECRET_OAUTH2_SCOPE。将你的出站 IP 加入白名单(最多 15 个地址/网段)——FatSecret 的 IP 限制不仅限于 token 端点:已在真实 Vercel 部署上确认,即使持有有效签发的 token,实际的
foods.searchAPI 调用本身也会从未列入白名单的 IP 被拒绝(错误码 21,"Invalid IP address detected")。因此,一次性 OAuth2 token 获取和每一次搜索/详情调用都必须来自白名单 IP。本地环境就是你机器自身的公网 IP(curl https://ifconfig.me)。在 Vercel 上,其 serverless 函数默认没有固定出站 IP,请参阅下文"Vercel 固定出站 IP"——在任何 Signed Request 工具能在生产环境工作之前,这是必需的。
运行一次本地开发服务器以冒烟测试搜索(阶段 2 只需要步骤 1):
npm install cp .env.example .env.local # fill in FATSECRET_CLIENT_ID/SECRET + the MCP_BEARER_TOKEN/OAuth trio vercel dev运行一次性三方 OAuth1 设置(除 5 个搜索/详情工具外的所有工具都需要)——参见下文阶段 3。
部署到 Vercel——参见"部署"一节,但请先阅读"Vercel 固定出站 IP"。
Vercel 固定出站 IP
Vercel 的 serverless 函数没有固定出站 IP,鉴于上述发现,这是一个问题——每一次 search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 调用(不仅仅是 token 获取)都需要来自白名单 IP。没有这一点,这五个工具在本地可以正常工作(你白名单的是你机器的 IP),但在生产环境中会以 FatSecret API error 21: Invalid IP address detected 失败。
解决方案:通过固定 IP 的 HTTP 代理路由这些请求。本服务器开箱即用地支持 Fixie:
在 usefixie.com 注册——免费的
tricycleFree套餐(每月 500 次请求/100MB,$0)对个人使用足够了,因为它只承载 FatSecret 的 Signed Request 流量,而非你的整个应用。注意该套餐的请求配额是真实的约束,不同于仅限应用的速率限制——如果你搜索频繁,请留意用量,接近上限时升级(commuter,$5/月/2,500 次请求)。复制 Fixie 提供的代理 URL(
http://fixie:<password>@<host>:<port>)。将其设置为
FIXIE_URL——在.env.local中用于本地通过代理测试,在 Vercel 环境变量中用于生产环境。普通本地开发时保持未设置(此时你的 IP 已直接白名单)——lib/fatsecret/appAuth.ts仅在存在FIXIE_URL时才通过代理路由。在 FatSecret 开发者控制台中将 Fixie 的固定 IP(显示在 Fixie 仪表盘上)加入白名单,这是对本地开发白名单 IP 的补充而非替代。
没有其他服务器到 FatSecret 的流量经过此代理——lib/fatsecret/oauth1.ts 中的 OAuth1(Signed & Delegated)请求不受 IP 限制,因此日记/体重/锻炼/资料工具完全不需要 FIXIE_URL。
本地开发
npm install
cp .env.example .env.local # fill in real values
vercel dev冒烟测试(替换 $MCP_BEARER_TOKEN):
curl -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer $MCP_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'应返回上述 17 个工具。缺少/错误 token 的请求应返回 401。
阶段 3:一次性三方 OAuth1 设置
除 search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 外的每个工具都需要绑定到你的 FatSecret 账户的 OAuth1 访问 token/secret。获取一次:
npm run fatsecret:oauth-setup此脚本(scripts/fatsecret-oauth-setup.ts)将:
向 FatSecret 请求一个未授权的请求 token。
打印授权 URL——打开它,登录 FatSecret 并批准。FatSecret 会显示一个确认码。
提示你粘贴该确认码,然后将其兑换为永久访问 token/secret。
将
FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET写入.env.local。
然后将同样的两个值添加到 Vercel 的环境变量中(.env.local 永远不会被部署)——参见下文"部署"。
根据 FatSecret 的文档,此访问 token 不会过期。如果它被撤销(例如你在 FatSecret 账户设置中移除了该应用的访问权限),只需重新运行脚本获取新的即可——参考 fitness-mcp 的 derive() 模式的精神:在这里丢失凭据并非灾难,而是一条命令即可修复,只是这次是交互式的,而非确定性的重新推导。
从一个易记的主密码生成面向 Claude 的密钥
MCP_BEARER_TOKEN、OAUTH_CLIENT_ID 和 OAUTH_CLIENT_SECRET(第①层——Claude ↔ 本服务器,与上述 FatSecret 凭据无关)都可以从单个主密码确定性推导,因此丢失存储值并非灾难——只需重新推导:
derive() {
if [ -z "$MASTER_PASSPHRASE" ]; then
printf "Master passphrase: "
read -rs MASTER_PASSPHRASE
echo
fi
echo -n "$1" | openssl dgst -sha256 -hmac "$MASTER_PASSPHRASE" -hex | awk '{print $2}'
}
derive "fatsecret-mcp:bearer-token" # → MCP_BEARER_TOKEN
derive "fatsecret-mcp:oauth-client-id" # → OAUTH_CLIENT_ID
derive "fatsecret-mcp:oauth-client-secret" # → OAUTH_CLIENT_SECRET标签字符串不是秘密(可以安全地保留在本 README 中)——只有密码是。使用相同密码再次运行 derive 总是产生相同的值。这不适用于 FatSecret 侧的凭据(FATSECRET_CLIENT_ID/SECRET、FATSECRET_CONSUMER_KEY/SECRET、FATSECRET_ACCESS_TOKEN/SECRET)——那些来自 FatSecret 开发者控制台和 OAuth1 设置脚本,而非此密码。
测试
三层测试,全部在 CI(.github/workflows/ci.yml)中每次 push/PR 时运行——都不需要真实的 FatSecret 密钥,因此在公开仓库中也能同样工作:
npm run test # unit + integration (vitest) — pure logic, plus the real Next.js
# route handler exercised with fetch mocked
npm run build
npm run test:e2e # starts a real `next start` server and hits it over real HTTP
# (node's built-in test runner, no extra dependency)单元测试(
lib/**/*.test.ts):bearer token 验证、OAuth2.1 代码签名/PKCE/redirect-URI 白名单(包含 RFC 7636 测试向量)、FatSecret OAuth2 Client Credentials token 获取/缓存/刷新(lib/fatsecret/appAuth.test.ts)、OAuth1 HMAC-SHA1 签名与独立重新实现交叉校验(lib/fatsecret/oauth1.test.ts),以及每个lib/fatsecret/*.ts响应结构归一化(单对象 vs 数组、数字字符串 vs 数字、空响应怪癖)。集成测试(
test/integration/*.test.ts):真实的app/api/mcp/route.ts处理器连接到真实的lib/fatsecret/*模块,仅 mockfetch,覆盖 OAuth2(Signed Request)和 OAuth1(Signed & Delegated)两条工具路径,以及每个写工具的确认门控;真实的/api/oauth/authorize//api/oauth/token路由;.well-knownOAuth 元数据路由。端到端测试(
test/e2e/*.e2e.test.mjs):启动生产构建并通过真实 HTTP 断言——健康检查、错误/缺失认证返回 401、tools/list返回全部 17 个工具、OAuth 发现元数据,以及完整的授权码 + PKCE 往返。不接触真实 FatSecret 数据(CI 按设计没有真实凭据)。
对照真实 FatSecret 账户手动验证
CI 从不接触真实 FatSecret 数据,而且——根据上文"未验证内容"——本服务器对 FatSecret 精确响应结构的一些假设从未对照真实账户检查过。注册并运行 OAuth1 设置脚本后,逐项完成此清单并修复发现的任何不匹配:
在.env.local中设置真实的FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET,运行vercel dev,用真实查询调用——已完成,已确认可对照真实账户工作。如果还没做,请对search_foodsget_food_detail也这样做——确认它返回合理的营养数值。类似地调用
search_recipes和get_recipe_detail。仍待确认。如果你的套餐包含
barcodescope,用真实产品的条形码调用find_food_by_barcode,确认响应结构与lib/fatsecret/foods.ts的RawFindIdForBarcodeResponse匹配——不匹配则修复。仍待确认。运行npm run fatsecret:oauth-setup,然后调用get_profile和——已完成。get_food_diaryget_food_diary完全匹配;get_profile缺少heightCm,现已修复——参见上文"未验证内容"。用
confirm: true和一个明显是一次性的条目调用create_food_diary_entry,然后对同一日期调用get_food_diary确认它出现且食物/份量/数量/餐次正确。然后对其调用update_food_diary_entry,再调用delete_food_diary_entry——确认每次都能往返。仍待确认——注意get_food_diary返回的meal是大写("Breakfast");在假设没问题之前,值得仔细确认create_food_diary_entry/update_food_diary_entry在写入时接受同样的大小写(或 FatSecret 写入侧实际期望的任何大小写)。如果你的套餐包含体重追踪,用
confirm: true调用update_weight,确认get_weight_history能反映它。仍待确认。create_exercise_entry和get_exercise_diary是本代码库中验证最少的配对。get_exercise_diary的方法/信封结构现已确认为真实,但揭示了锻炼日记的数据模型比预想更复杂(参见上文"未验证内容")——在信任create_exercise_entry之前,先在 FatSecret 应用中手动记录一次锻炼,重新检查get_exercise_diary,看手动条目是否像食物条目那样带有exercise_entry_id/date_int;这会告诉你"可单独创建的条目"在这里是否是正确模型,然后再对真实数据尝试create_exercise_entry本身。永远不要提交真实的 FatSecret 凭据,也永远不要在 CI 中运行此清单。
环境变量
变量 | 用途 |
| OAuth 2.0 客户端凭据 — 签名请求方法(搜索/详情工具) |
| 可选。以空格分隔的 OAuth2 作用域,默认为 |
| 可选。默认为 |
| 可选。用于 OAuth2 令牌获取和每次签名请求调用的固定 IP HTTP 代理 URL( |
| OAuth 1.0 消费者密钥/密钥 — 用于签署一次性设置脚本以及每次签名与委托调用 |
| 用于您的 FatSecret 账户的 OAuth 1.0 访问令牌/密钥 — 通过 |
| 此服务器在每个请求上要求的共享密钥,也是我们的 OAuth 流程签发的 access_token |
| 此服务器自身最小化 OAuth 授权服务器的凭据 |
| 可选。 |
| 可选。用于认证失败实时告警的 Slack/Discord 传入 webhook URL — 请参阅上文"安全事件日志与告警"。无论是否设置此项,失败信息始终记录到 |
在 Vercel 项目的环境变量(生产环境 + 预览环境)中设置这些变量。切勿提交真实值 — .env.example 仅记录变量名。
部署
vercel linkvercel env add FATSECRET_CLIENT_ID(对上面表格中您有值的每个变量重复此操作 — 至少包括FATSECRET_CLIENT_ID/SECRET、MCP_BEARER_TOKEN、OAUTH_CLIENT_ID/SECRET;按照上文"为 Vercel 配置固定出站 IP"添加FIXIE_URL— 实际上这是必需的,而非可选的;运行 OAuth1 设置脚本后,再添加FATSECRET_CONSUMER_*/FATSECRET_ACCESS_TOKEN*这对变量)在部署之前(即在下文第 4 步之前),将 Vercel 项目的 Node.js 版本设置为 22.19 或更高(项目 → 设置 → 常规 → Node.js 版本,或当前 Vercel 控制台中对应的位置)。此服务器的
undici@8依赖(用于 Fixie 代理 — 请参阅上文"为 Vercel 配置固定出站 IP")声明了"engines": {"node": ">=22.19.0"},package.json自身的engines字段也记录了相同的要求 — 但两者在 Vercel 上都不会单独强制执行,因此仍固定使用较旧 Node 版本(例如 20.x)的项目会"成功"部署,然后在运行时失败。在 Vercel 控制台中连接此 GitHub 仓库,以便在推送到
main时自动部署,或手动运行vercel --prod。记下部署后的 URL(查看项目 → 设置 → 域名 — 此项目的生产 URL 恰好是未被占用的
https://fatsecret-mcp.vercel.app,但那是 Vercel 的共享命名空间,所以不要假设分叉后它仍然可用)。在 FatSecret 开发者控制台中将 Fixie 的固定 IP 加入白名单(请参阅上文"为 Vercel 配置固定出站 IP")— 这是生产环境中最容易出问题的一步,因为如果没有它,
search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode都会以FatSecret API error 21失败。
连接到 Claude
自定义连接器只能从 claude.ai(网页版)或桌面应用添加 — 不能从移动应用添加。添加后,它们会自动在移动端可用。
在 claude.ai 上:设置 → 连接器 → 添加自定义连接器。
名称:
FatSecret。URL:https://<your-deployment>/api/mcp。如果您的账户拥有"请求头"测试版功能:在那里添加
Authorization: Bearer <MCP_BEARER_TOKEN>并跳到第 5 步。否则,打开高级设置,填入在 Vercel 中设置的
OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET值作为 OAuth 客户端 ID / OAuth 客户端密钥。Claude 将通过此服务器的.well-known元数据自动发现/authorize和/token端点。保存。Claude 应列出上述 17 个工具。
试试这样问:"バナナのカロリーを教えて"(告诉我一根香蕉的卡路里),或"今日の朝食にバナナを1本記録して"(记录今天早餐吃的一根香蕉 — 在阶段 3/4 设置并验证之后)。
致谢
三腿 OAuth1 流程设计参考了 fcoury/fatsecret-mcp(MIT 许可证),该项目将 OAuth 流程本身暴露为 MCP 工具;本项目则将其作为独立的设置脚本(scripts/fatsecret-oauth-setup.ts)运行一次,因为它是为单个个人 FatSecret 账户而非多用户使用而构建的。未从中复制任何代码。
This server cannot be deployed
Maintenance
Related MCP Connectors
Unlock the power of food transparency with our Open Food Facts MCP server. Easily look up any food
MCP server exposing supplements database used by iNutriPlan.com
A calorie MCP that looks up calories and macros from a 4M+ food catalog, not model guesses.
- mcpOAuthcom.zomato
An MCP server that exposes functionalities to use Zomato's services.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server for managing food diary, nutrition tracking, meal planning, and weight logging via the FatSecret Platform API.15MIT
- AlicenseNot gradedqualityBmaintenanceA remote MCP server for personal nutrition tracking that enables logging meals, tracking macros, and reviewing nutrition history through conversation.38 npm66MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for USDA nutrition data lookup, meal logging, and daily macro tracking.38 npmMIT
- AlicenseNot gradedqualityDmaintenanceA remote MCP server for personal nutrition tracking that lets you log meals, track macros, water, and body weight, and review your nutrition history through conversation.38 npmMIT