Skip to main content
Glama
ikeike443
by ikeike443

fatsecret-mcp

CI

一个个人远程 MCP(Model Context Protocol)服务器,让 Claude 可以直接在对话中搜索 FatSecret 的食物/食谱数据库,并读写你自己的食物日记、体重和运动日志。部署在 Vercel 的免费 Hobby 层级。是 fitness-mcp(Hevy)的姊妹项目 — 每个产品对应一个 MCP 服务器,共享相同的认证模式。

许可证

MIT

状态

  • 搜索(Phase 2):已实现 — search_foods, get_food_detail, search_recipes, get_recipe_detail, find_food_by_barcode。无需 FatSecret 用户授权;只需要来自 FatSecret 开发者控制台的 OAuth 2.0 客户端 ID/密钥。

  • 日记/体重/运动/个人资料(Phase 4):已实现,但未经真实 FatSecret 账户验证 — 构建此项目时尚无 FatSecret API 注册(见下文“尚未验证的内容”)。在依赖它之前,请对照真实账户确认每个方法的确切字段名,如有出入请更新代码/测试。

  • 3-legged OAuth1 设置脚本(Phase 3):已实现(scripts/fatsecret-oauth-setup.ts),尚未针对真实 FatSecret 账户运行。

两层认证

该服务器位于 Claude 和 FatSecret 之间,这两段关系的认证方式完全不同 — 这是接触代码前需要理解的关键。

Claude  <──①── this server (fatsecret-mcp)  ──②──>  FatSecret API

① Claude ↔ 本服务器 — 使用一个共享密钥,模式与 fitness-mcp 相同。Claude 在每个请求上发送 Authorization: Bearer <MCP_BEARER_TOKEN>lib/auth.ts 会检查它。由于 Claude 的 static-header 选项仍处于 beta 限制,本服务器还运行自己的最小化 OAuth 2.1 授权服务器(lib/oauth.ts/api/oauth/authorize/api/oauth/token),因此 Claude 标准的 OAuth 客户端 ID/密钥字段可作为始终可用的后备方案 — 完整理由见 fitness-mcp 的 README,此处同样适用。

② 本服务器 ↔ FatSecret — 这正是比 fitness-mcp 更复杂的地方,因为 FatSecret 本身对两类 API 方法使用两种不同的 OAuth 版本,这一点无法回避 — 这是 FatSecret API 的设计,而非这里所做的选择:

FatSecret 方法类别

示例方法

本服务器的认证方式

签名请求(不涉及特定用户)

foods.search, food.get, recipes.search, recipe.get, food.find_id_for_barcode

OAuth 2.0 客户端凭据lib/fatsecret/appAuth.tsoauth.fatsecret.com 获取并缓存应用级 bearer 令牌。完全自动;一次性开发者注册后无需任何人工交互。

签名并委派请求(读取/写入你的 FatSecret 账户)

food_entries.*, food_entry.*, weights.get_month, weight.update, exercise_entries.*, profile.get, foods.get_favorites

OAuth 1.0a、3-legged、HMAC-SHA1 签名 — lib/fatsecret/oauth1.ts。FatSecret 对这些方法根本不支持 OAuth 2.0,因此这里无法避免 OAuth1。这需要一次一次性交互式授权(见下方 Phase 3),在浏览器中登录 FatSecret 并批准此应用;所获得的访问令牌/密钥之后会被自动永久复用(见 Phase 3 下的注意事项)。

具体来说:search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 只要注册了 FatSecret 应用并设置 FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET 即可使用。其他所有工具还需要 FATSECRET_CONSUMER_KEY/FATSECRET_CONSUMER_SECRET(OAuth1 — 来自同一 FatSecret 应用的另一对凭据)和 FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET(通过运行一次设置脚本获得)。

工具列表

工具

类型

所需认证

描述

search_foods

读取

OAuth2(应用)

按名称搜索 FatSecret 的食物数据库

get_food_detail

读取

OAuth2(应用)

获取单个食物的完整每份营养信息

search_recipes

读取

OAuth2(应用)

搜索 FatSecret 的食谱数据库

get_recipe_detail

读取

OAuth2(应用)

获取单个食谱的完整食材/步骤

find_food_by_barcode

读取

OAuth2(应用)

将 GTIN-13 条码解析为 foodId — 需要 barcode 作用域,可能仅限 Premier

get_food_diary

读取

OAuth1(用户)

列出某一日期的食物日记条目

get_favorite_foods

读取

OAuth1(用户)

列出收藏的食物

get_most_eaten_foods

读取

OAuth1(用户)

列出最常吃的食物,可按餐次筛选

get_recently_eaten_foods

读取

OAuth1(用户)

列出最近吃过的食物,可按餐次筛选

get_weight_history

读取

OAuth1(用户)

列出某一月份的体重条目 — 可能仅限 Premier

get_exercise_diary

读取

OAuth1(用户)

列出某一日期的运动日记条目

get_profile

读取

OAuth1(用户)

获取用户的 FatSecret 个人资料摘要

create_food_diary_entry

写入

OAuth1(用户)

将食物记入日记

update_food_diary_entry

写入

OAuth1(用户)

更新现有日记条目

delete_food_diary_entry

写入

OAuth1(用户)

删除日记条目

update_weight

写入

OAuth1(用户)

记录/更新体重条目 — 可能仅限 Premier

create_exercise_entry

写入

OAuth1(用户)

记录运动条目

写入工具默认进行 dry-run

与 fitness-mcp 相同的设计:每个写入工具都需要 confirm: true 参数。其描述会指示调用方 LLM 先向用户准确展示将要写入的内容,并取得明确的同意。这是一种结构性的提示,而不是保证 — 决定是否调用工具的同一个 LLM 也会设置 confirm,而且在认证层没有读写工具之间的作用域分隔,因此任何持有有效 MCP_BEARER_TOKEN 的调用者都可以调用任何工具。

尚未验证的内容

构建此项目时尚无 FatSecret API 注册(这一步需要人工操作 — 见下方“设置”),因此:

  • search_foods/get_food_detail/search_recipes/get_recipe_detail/profile.get/food_entries.get/weights.get_month 的方法名和核心参数已对照可用的第三方 FatSecret 客户端实现确认(非猜测)— 来源见 git 历史。

  • food.find_id_for_barcode 的响应结构、weight.update 的参数名以及所有 exercise_entries.* 都是尽力而为的重构,其推断理由已在 lib/fatsecret/*.ts 中内联标注。请将这些视为可靠的起点,而非经过验证的事实。

  • 注册后请对照真实账户运行下面的手动验证清单,并修正你发现的任何字段名不匹配问题(lib/fatsecret/*.test.ts 中的单元测试也需要相应更新)。

设置

  1. https://platform.fatsecret.com/ 注册 FatSecret Platform API 应用。你将获得:

    • OAuth 2.0 客户端 ID/密钥(用于 FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET)。

    • OAuth 1.0 Consumer Key/Secret(用于 FATSECRET_CONSUMER_KEY/FATSECRET_CONSUMER_SECRET)— 这是来自同一应用的独立一对凭据,不同于上面的 OAuth2 凭据。

    • 检查你的套餐包含哪些作用域(basic / premier / barcode / ...)— 据报道,weights.get_month/weight.update/find_food_by_barcode 需要 Premier 或 barcode/premier 作用域;请根据你的套餐确认,并在需要时调整 FATSECRET_OAUTH2_SCOPE

    • 为 OAuth2 令牌请求将你的出口 IP 加入白名单 — FatSecret 要求这样做(最多 15 个地址/范围)。如果部署到 Vercel,则需要一个静态出口 IP(例如通过 Vercel 支持的出口代理/插件);Vercel 默认的 serverless 函数没有固定 IP。

  2. 运行一次本地开发服务器以冒烟测试搜索(Phase 2 只需要第 1 步):

    npm install
    cp .env.example .env.local   # fill in FATSECRET_CLIENT_ID/SECRET + the MCP_BEARER_TOKEN/OAuth trio
    vercel dev
  3. 运行一次性的 3-legged OAuth1 设置(除 5 个搜索/详情工具外的每个工具都需要)— 见下方 Phase 3。

  4. 部署到 Vercel — 见下方 Deploy。

本地开发

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 个工具。缺少或错误的令牌请求应返回 401

Phase 3:一次性 3-legged OAuth1 设置

search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 之外的每个工具都需要一个绑定到你的 FatSecret 账户的 OAuth1 访问令牌/密钥。只需获取一次:

npm run fatsecret:oauth-setup

该脚本(scripts/fatsecret-oauth-setup.ts)将:

  1. 向 FatSecret 请求一个未授权的请求令牌。

  2. 打印授权 URL — 打开它,登录 FatSecret 并批准。FatSecret 会显示一个确认码。

  3. 提示你粘贴该确认码,然后将其换取为永久的访问令牌/密钥。

  4. FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET 写入 .env.local

然后将这两个值也添加到 Vercel 的环境变量中(.env.local 永远不会被部署)— 见下方 Deploy。

根据 FatSecret 的文档,此访问令牌不会过期。如果它被撤销(例如你在 FatSecret 账户设置中移除了该应用的访问权限),只需重新运行脚本即可获取新的令牌——这正是 fitness-mcp 的 derive() 模式所体现的精神:在这里丢失凭据并非灾难,它只是需要一条命令即可修复,只是这次是交互式的,而非确定性的重新派生。

从一个易记的密码短语生成面向 Claude 的密钥

MCP_BEARER_TOKENOAUTH_CLIENT_IDOAUTH_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/SECRETFATSECRET_CONSUMER_KEY/SECRETFATSECRET_ACCESS_TOKEN/SECRET)——这些来自 FatSecret 开发者控制台和 OAuth1 设置脚本,而非此密码短语。

测试

三层测试,均在每次推送/PR 时于 CI(.github/workflows/ci.yml)中运行——都不需要真实的 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 令牌验证、OAuth2.1 代码签名/PKCE/重定向 URI 白名单(包含 RFC 7636 测试向量)、FatSecret OAuth2 客户端凭据令牌获取/缓存/刷新(lib/fatsecret/appAuth.test.ts)、OAuth1 HMAC-SHA1 签名与独立重新实现的交叉验证(lib/fatsecret/oauth1.test.ts),以及每个 lib/fatsecret/*.ts 响应形状的标准化(单对象与数组、数字字符串与数字、空响应等特殊情况)。

  • 集成测试test/integration/*.test.ts):将真实的 app/api/mcp/route.ts 处理器连接到真实的 lib/fatsecret/* 模块,仅模拟 fetch,覆盖 OAuth2(Signed Request)和 OAuth1(Signed & Delegated)工具路径,以及对每个写工具的确认门控;真实的 /api/oauth/authorize//api/oauth/token 路由;.well-known OAuth 元数据路由。

  • 端到端测试test/e2e/*.e2e.test.mjs):启动生产构建并通过真实 HTTP 进行断言——健康检查、无效/缺失认证时返回 401、tools/list 返回全部 17 个工具、OAuth 发现元数据,以及完整的授权码 + PKCE 往返。不涉及真实 FatSecret 数据(CI 设计上就没有真实凭据)。

使用真实 FatSecret 账户手动验证

CI 从不接触真实的 FatSecret 数据,而且——按照上文“尚未验证的内容”——本服务器对 FatSecret 精确响应形状的一些假设尚未针对真实账户进行过任何检查。完成注册并运行 OAuth1 设置脚本后,请按此清单逐项检查并修复你发现的任何不匹配之处:

  1. .env.local 中设置真实的 FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET,运行 vercel dev,并使用真实查询调用 search_foods(例如通过上面的冒烟测试 curl 模式,使用 tools/call)——确认返回了真实结果,并且对其中的一项调用 get_food_detail 能返回合理的营养数据。

  2. 类似地调用 search_recipesget_recipe_detail

  3. 如果你的套餐包含 barcode 范围,请使用真实产品的条形码调用 find_food_by_barcode,并确认响应形状与 lib/fatsecret/foods.tsRawFindIdForBarcodeResponse 匹配——如果不匹配则修复它。

  4. 运行 npm run fatsecret:oauth-setup,然后调用 get_profileget_food_diary——确认 lib/fatsecret/profile.ts/lib/fatsecret/diary.ts 中的字段名与真实响应匹配(这些字段名是根据文档重建的,并非抓取所得)。

  5. 使用 confirm: true 和一条明显是临时测试的条目调用 create_food_diary_entry,然后对同一日期调用 get_food_diary,确认它显示出正确的食物/份量/数量/餐次。然后调用 update_food_diary_entry 更新它,再调用 delete_food_diary_entry 删除它——确认每一步都能正常往返。

  6. 如果你的套餐包含体重跟踪,请使用 confirm: true 调用 update_weight,并确认 get_weight_history 能反映该更新。

  7. create_exercise_entryget_exercise_diary 是此代码库中验证最少的一对(参见 lib/fatsecret/exercise.ts 顶部的警告)——在依赖它之前,请对照 https://platform.fatsecret.com/docs/guides 确认准确的方法名/参数;它可能需要真正的修复,而不仅仅是验证。

  8. 切勿提交真实的 FatSecret 凭据,也切勿在 CI 中运行此清单。

环境变量

变量

用途

FATSECRET_CLIENT_ID / FATSECRET_CLIENT_SECRET

OAuth 2.0 客户端凭据 —— Signed Request 方法(搜索/详情工具)

FATSECRET_OAUTH2_SCOPE

可选。以空格分隔的 OAuth2 作用域,默认 basic。根据需要添加 barcode/premier

FATSECRET_FOOD_GET_METHOD

可选。默认为 food.get.v4;如果你的套餐缺少 v4 访问权限,可覆盖(例如 food.get

FATSECRET_CONSUMER_KEY / FATSECRET_CONSUMER_SECRET

OAuth 1.0 消费者密钥/消费者机密 —— 用于签署一次性设置脚本以及每次 Signed & Delegated 调用

FATSECRET_ACCESS_TOKEN / FATSECRET_ACCESS_TOKEN_SECRET

OAuth 1.0 访问令牌/机密,用于你的 FatSecret 账户——通过 npm run fatsecret:oauth-setup(第 3 阶段)获取

MCP_BEARER_TOKEN

本服务器在每个请求上要求的共享机密,也是我们 OAuth 流程颁发的 access_token

OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET

本服务器自身极简 OAuth 授权服务器的凭据

OAUTH_ALLOWED_REDIRECT_HOSTS

可选。/api/oauth/authorizeredirect_uri 的逗号分隔白名单。默认为 claude.ai,claude.com

在 Vercel 项目的环境变量(Production + Preview)中设置这些变量。切勿提交真实值——.env.example 仅记录这些名称。

部署

  1. vercel link

  2. vercel env add FATSECRET_CLIENT_ID(对上面表格中你已有值的每个变量重复此操作——至少包括 FATSECRET_CLIENT_ID/SECRETMCP_BEARER_TOKENOAUTH_CLIENT_ID/SECRET;一旦运行了 OAuth1 设置脚本,再添加 FATSECRET_CONSUMER_*/FATSECRET_ACCESS_TOKEN* 这一对)

  3. 在 Vercel 仪表板中连接此 GitHub 仓库,以便在推送到 main 时自动部署,或手动运行 vercel --prod

  4. 记下部署后的 URL(检查项目 → 设置 → 域名,因为 fatsecret-mcp.vercel.app 可能已被 Vercel 的共享命名空间占用)。

  5. 在 FatSecret 开发者控制台中将该部署的出站 IP 加入白名单,用于 OAuth2 令牌请求(参见设置步骤 1)——这是在生产环境中最可能出问题的一步,因为 Vercel 无服务器函数默认没有固定 IP。

连接到 Claude

自定义连接器只能从 claude.ai(网页版)或桌面应用添加——不能从移动应用添加。一旦在那边添加,它们就会自动在移动端可用。

  1. 在 claude.ai 上:设置 → 连接器 → 添加自定义连接器。

  2. 名称:FatSecret。URL:https://<your-deployment>/api/mcp

  3. 如果你的账户拥有“请求标头”测试版功能:在此处添加 Authorization: Bearer <MCP_BEARER_TOKEN>,然后跳到第 5 步。

  4. 否则,打开“高级设置”,用 Vercel 中设置的 OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET 值填写 OAuth 客户端 ID / OAuth 客户端密钥。Claude 将通过本服务器的 .well-known 元数据自动发现 /authorize/token 端点。

  5. 保存。Claude 应列出上述 17 个工具。

试试问:“バナナのカロリーを教えて”(告诉我一根香蕉的卡路里),或“今日の朝食にバナナを1本記録して”(记录一根香蕉作为今天的早餐——在第 3/4 阶段设置并验证之后)。

致谢

三方 OAuth1 流程设计的灵感来自 fcoury/fatsecret-mcp(MIT 许可),该项目将 OAuth 流程本身暴露为 MCP 工具;而本项目则将其作为一次性独立设置脚本(scripts/fatsecret-oauth-setup.ts)运行,因为本项目是为单个个人 FatSecret 账户而非多用户使用而构建的。未从中复制任何代码。

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.

  • MCP server for Withings health data — sleep, activity, heart, and body metrics.

  • GibsonAI MCP server: manage your databases with natural language

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ikeike443/fatsecret-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server