Skip to main content
Glama
ikeike443
by ikeike443

fatsecret-mcp

CI

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

License

MIT

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

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

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

OAuth 2.0 客户端凭证 — lib/fatsecret/appAuth.ts 从 oauth.fatsecret.com 获取并缓存应用级 bearer token。完全自动;一次性开发者注册后无需人工交互。

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

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

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

具体来说:一旦你注册了 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 未配置。

两个独立层,因此可以优雅降级:

  1. 始终记录。 上述每次失败都会通过 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 直接断言了这一点。

  2. 可选实时告警。 如果设置了 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

search_foods

read

OAuth2 (app)

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

get_food_detail

read

OAuth2 (app)

一种食物的完整每份营养信息

search_recipes

read

OAuth2 (app)

搜索 FatSecret 的食谱数据库

get_recipe_detail

read

OAuth2 (app)

一种食谱的完整配料/做法

find_food_by_barcode

read

OAuth2 (app)

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

get_food_diary

read

OAuth1 (user)

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

get_favorite_foods

read

OAuth1 (user)

列出收藏的食物

get_most_eaten_foods

read

OAuth1 (user)

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

get_recently_eaten_foods

read

OAuth1 (user)

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

get_weight_history

read

OAuth1 (user)

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

get_exercise_diary

read

OAuth1 (user)

列出某日期的运动条目

get_profile

read

OAuth1 (user)

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

create_food_diary_entry

write

OAuth1 (user)

将食物记录到日记

update_food_diary_entry

write

OAuth1 (user)

更新现有日记条目

delete_food_diary_entry

write

OAuth1 (user)

删除日记条目

update_weight

write

OAuth1 (user)

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

create_exercise_entry

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 中的单元测试需要同步更新)。

设置

  1. 在 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/premier scope;请对照自己的套餐确认,必要时调整 FATSECRET_OAUTH2_SCOPE。

    • 将你的出站 IP 加入白名单(最多 15 个地址/网段)——FatSecret 的 IP 限制不仅限于 token 端点:已在真实 Vercel 部署上确认,即使持有有效签发的 token,实际的 foods.search API 调用本身也会从未列入白名单的 IP 被拒绝(错误码 21,"Invalid IP address detected")。因此,一次性 OAuth2 token 获取和每一次搜索/详情调用都必须来自白名单 IP。本地环境就是你机器自身的公网 IP(curl https://ifconfig.me)。在 Vercel 上,其 serverless 函数默认没有固定出站 IP,请参阅下文"Vercel 固定出站 IP"——在任何 Signed Request 工具能在生产环境工作之前,这是必需的。

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

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

  4. 部署到 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:

  1. 在 usefixie.com 注册——免费的 tricycleFree 套餐(每月 500 次请求/100MB,$0)对个人使用足够了,因为它只承载 FatSecret 的 Signed Request 流量,而非你的整个应用。注意该套餐的请求配额是真实的约束,不同于仅限应用的速率限制——如果你搜索频繁,请留意用量,接近上限时升级(commuter,$5/月/2,500 次请求)。

  2. 复制 Fixie 提供的代理 URL(http://fixie:<password>@<host>:<port>)。

  3. 将其设置为 FIXIE_URL——在 .env.local 中用于本地通过代理测试,在 Vercel 环境变量中用于生产环境。普通本地开发时保持未设置(此时你的 IP 已直接白名单)——lib/fatsecret/appAuth.ts 仅在存在 FIXIE_URL 时才通过代理路由。

  4. 在 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)将:

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

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

  3. 提示你粘贴该确认码,然后将其兑换为永久访问 token/secret。

  4. 将 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/* 模块,仅 mock 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——已完成,已确认可对照真实账户工作。如果还没做,请对 get_food_detail 也这样做——确认它返回合理的营养数值。

  2. 类似地调用 search_recipes 和 get_recipe_detail。仍待确认。

  3. 如果你的套餐包含 barcode scope,用真实产品的条形码调用 find_food_by_barcode,确认响应结构与 lib/fatsecret/foods.ts 的 RawFindIdForBarcodeResponse 匹配——不匹配则修复。仍待确认。

  4. 运行 npm run fatsecret:oauth-setup,然后调用 get_profile 和 get_food_diary——已完成。get_food_diary 完全匹配;get_profile 缺少 heightCm,现已修复——参见上文"未验证内容"。

  5. 用 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 写入侧实际期望的任何大小写)。

  6. 如果你的套餐包含体重追踪,用 confirm: true 调用 update_weight,确认 get_weight_history 能反映它。仍待确认。

  7. create_exercise_entry 和 get_exercise_diary 是本代码库中验证最少的配对。get_exercise_diary 的方法/信封结构现已确认为真实,但揭示了锻炼日记的数据模型比预想更复杂(参见上文"未验证内容")——在信任 create_exercise_entry 之前,先在 FatSecret 应用中手动记录一次锻炼,重新检查 get_exercise_diary,看手动条目是否像食物条目那样带有 exercise_entry_id/date_int;这会告诉你"可单独创建的条目"在这里是否是正确模型,然后再对真实数据尝试 create_exercise_entry 本身。

  8. 永远不要提交真实的 FatSecret 凭据,也永远不要在 CI 中运行此清单。

环境变量

变量

用途

FATSECRET_CLIENT_ID / FATSECRET_CLIENT_SECRET

OAuth 2.0 客户端凭据 — 签名请求方法(搜索/详情工具)

FATSECRET_OAUTH2_SCOPE

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

FATSECRET_FOOD_GET_METHOD

可选。默认为 food.get.v4;如果您的套餐不支持 v4 访问,可覆盖(例如 food.get)

FIXIE_URL

可选。用于 OAuth2 令牌获取和每次签名请求调用的固定 IP HTTP 代理 URL(http://fixie:<password>@<host>:<port>)— 在 Vercel 上必需,因为 Vercel 默认没有固定的出站 IP。请参阅上文"为 Vercel 配置固定出站 IP"。本地开发时请留空。

FATSECRET_CONSUMER_KEY / FATSECRET_CONSUMER_SECRET

OAuth 1.0 消费者密钥/密钥 — 用于签署一次性设置脚本以及每次签名与委托调用

FATSECRET_ACCESS_TOKEN / FATSECRET_ACCESS_TOKEN_SECRET

用于您的 FatSecret 账户的 OAuth 1.0 访问令牌/密钥 — 通过 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/authorize 的 redirect_uri 的逗号分隔白名单。默认为 claude.ai,claude.com

SECURITY_ALERT_WEBHOOK_URL

可选。用于认证失败实时告警的 Slack/Discord 传入 webhook URL — 请参阅上文"安全事件日志与告警"。无论是否设置此项,失败信息始终记录到 stderr

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

部署

  1. vercel link

  2. vercel 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* 这对变量)

  3. 在部署之前(即在下文第 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)的项目会"成功"部署,然后在运行时失败。

  4. 在 Vercel 控制台中连接此 GitHub 仓库,以便在推送到 main 时自动部署,或手动运行 vercel --prod。

  5. 记下部署后的 URL(查看项目 → 设置 → 域名 — 此项目的生产 URL 恰好是未被占用的 https://fatsecret-mcp.vercel.app,但那是 Vercel 的共享命名空间,所以不要假设分叉后它仍然可用)。

  6. 在 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(网页版)或桌面应用添加 — 不能从移动应用添加。添加后,它们会自动在移动端可用。

  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 账户而非多用户使用而构建的。未从中复制任何代码。

Related MCP Connectors

Related MCP Servers