Skip to main content
Glama
ikeike443
by ikeike443

fatsecret-mcp

CI

개인 원격 MCP(Model Context Protocol) 서버로, Claude가 FatSecret의 음식/레시피 데이터베이스를 검색하고 대화 중에 직접 자신의 음식 일기, 체중, 운동 기록을 읽고 쓸 수 있게 해줍니다. Vercel의 무료 Hobby 티어에 배포됩니다. fitness-mcp(Hevy)의 자매 프로젝트로, 제품당 하나의 MCP 서버를 두고 동일한 인증 패턴을 공유합니다.

라이선스

MIT

상태

  • 검색(2단계): 구현됨 — search_foods, get_food_detail, search_recipes, get_recipe_detail, find_food_by_barcode. FatSecret 사용자 인증이 필요 없으며, FatSecret 개발자 콘솔의 OAuth 2.0 Client ID/Secret만 있으면 됩니다.

  • 일기/체중/운동/프로필(4단계): 구현됨, 그러나 실제 FatSecret 계정으로 검증되지 않음 — 이 프로젝트를 만들 당시 FatSecret API 등록이 없었기 때문입니다(아래 “검증되지 않은 사항” 참조). 실제 계정으로 각 메서드의 정확한 필드 이름을 확인한 후 의존하고, 잘못된 부분이 있으면 코드/테스트를 업데이트하세요.

  • 3-legged OAuth1 설정 스크립트(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의 정적 헤더 옵션이 아직 베타로 제한되어 있기 때문에, 이 서버는 자체적인 최소 OAuth 2.1 인증 서버(lib/oauth.ts, /api/oauth/authorize, /api/oauth/token)도 운영하여 Claude의 표준 OAuth Client ID/Secret 필드가 항상 사용 가능한 폴백으로 작동하게 합니다. 자세한 이유는 fitness-mcp의 README를 참조하세요. 여기에도 동일하게 적용됩니다.

② 이 서버 ↔ FatSecret — 여기가 fitness-mcp보다 더 복잡한 부분입니다. FatSecret 자체가 두 종류의 API 메서드에 대해 서로 다른 두 가지 OAuth 버전을 사용하기 때문이며, 이를 피할 방법은 없습니다. 이는 여기서 내린 선택이 아니라 FatSecret API가 설계된 방식입니다.

FatSecret 메서드 분류

예시 메서드

이 서버의 인증 방식

Signed Request (특정 사용자 미관여)

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

OAuth 2.0 Client Credentialslib/fatsecret/appAuth.tsoauth.fatsecret.com에서 앱 수준 bearer 토큰을 가져와 캐시합니다. 완전 자동이며, 일회성 개발자 등록 이후에는 사람의 개입이 필요 없습니다.

Signed & Delegated Request (당신의 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을 피할 방법은 없습니다. 이는 일회성 대화형 인증(아래 3단계)이 필요하며, 브라우저에서 FatSecret에 로그인하고 이 앱을 승인해야 합니다. 그 결과로 얻은 액세스 토큰/시크릿은 이후 자동으로 계속 재사용됩니다(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 (앱)

한 음식의 1회 제공량당 전체 영양 정보

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 (사용자)

운동 항목 기록

쓰기 도구는 기본적으로 드라이런입니다

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 Client ID/Secret(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의 기본 서버리스 함수는 고정 IP가 없습니다.

  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. 일회성 3-legged OAuth1 설정을 실행하세요(5개의 검색/상세 도구를 제외한 모든 도구에 필요) — 아래 3단계를 참조하세요.

  4. Vercel에 배포하세요 — 아래 배포를 참조하세요.

로컬 개발

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을 받아야 합니다.

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을 출력합니다 — URL을 열고 FatSecret에 로그인한 뒤 승인합니다. FatSecret이 확인 코드를 보여줍니다.

  3. 그 코드를 붙여넣도록 요청한 다음, 이를 영구 액세스 토큰/시크릿으로 교환합니다.

  4. FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET.env.local에 기록합니다.

그런 다음 동일한 두 값을 Vercel의 환경 변수에도 추가하세요(.env.local은 배포되지 않습니다) — 아래 배포를 참조하세요.

FatSecret 문서에 따르면 이 액세스 토큰은 만료되지 않습니다. 만약 토큰이 취소되면(예: 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 설정 스크립트에서 얻은 것입니다.

테스트

세 개의 레이어가 모든 push/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/redirect-URI 허용 목록(RFC 7636 테스트 벡터 포함), FatSecret OAuth2 Client Credentials 토큰 가져오기/캐시/갱신(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/* 모듈에 연결하고 fetch만 모의 처리하여 OAuth2(Signed Request)와 OAuth1(Signed & Delegated) 도구 경로를 모두 다루며, 모든 쓰기 도구에 confirm 게이팅을 적용합니다. 실제 /api/oauth/authorize//api/oauth/token 라우트와 .well-known OAuth 메타데이터 라우트도 포함합니다.

  • E2E(test/e2e/*.e2e.test.mjs): 프로덕션 빌드를 부팅하고 실제 HTTP로 검증합니다 — 상태 확인, 잘못되었거나 누락된 인증 시 401, tools/list가 17개 도구를 모두 반환하는지, OAuth 검색 메타데이터, 그리고 전체 authorization-code + 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 Client Credentials — 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 Consumer Key/Secret — 일회성 설정 스크립트와 모든 Signed & Delegated 호출에 서명

FATSECRET_ACCESS_TOKEN / FATSECRET_ACCESS_TOKEN_SECRET

OAuth 1.0 access token/secret — 사용자 본인의 FatSecret 계정용. npm run fatsecret:oauth-setup(Phase 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/SECRET, MCP_BEARER_TOKEN, OAUTH_CLIENT_ID/SECRET; OAuth1 설정 스크립트를 실행한 후에는 FATSECRET_CONSUMER_*/FATSECRET_ACCESS_TOKEN* 쌍도 추가)

  3. Vercel 대시보드에서 이 GitHub 저장소를 연결하여 main에 푸시할 때 자동 배포되게 하거나, 수동으로 vercel --prod를 실행하세요.

  4. 배포된 URL을 확인하세요(프로젝트 → 설정 → 도메인을 확인. fatsecret-mcp.vercel.app은 Vercel의 공유 네임스페이스에서 이미 사용 중일 수 있습니다).

  5. 해당 배포의 아웃바운드 IP를 허용 목록에 추가하세요. FatSecret 개발자 콘솔에서 OAuth2 토큰 요청용으로(설정 1단계 참조) — Vercel 서버리스 함수는 기본적으로 고정 IP가 없으므로 이 단계가 프로덕션에서 가장 문제가 될 가능성이 높습니다.

Claude에 연결

사용자 지정 커넥터는 claude.ai(웹) 또는 데스크톱 앱에서만 추가할 수 있습니다. 모바일 앱에서는 추가할 수 없습니다. 일단 추가되면 모바일에서 자동으로 사용할 수 있습니다.

  1. claude.ai에서: 설정 → 커넥터 → 사용자 지정 커넥터 추가.

  2. 이름: FatSecret. URL: https://<your-deployment>/api/mcp.

  3. 계정에 "Request headers" 베타 기능이 있다면: 거기에 Authorization: Bearer <MCP_BEARER_TOKEN>을 추가하고 5단계로 건너뛰세요.

  4. 그렇지 않으면 고급 설정을 열고 Vercel에 설정된 OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET 값으로 OAuth Client ID / OAuth Client Secret을 입력하세요. Claude는 이 서버의 .well-known 메타데이터를 통해 /authorize/token 엔드포인트를 자동으로 발견합니다.

  5. 저장하세요. Claude는 위의 17개 도구를 나열해야 합니다.

다음과 같이 물어보세요: "バナナのカロリーを教えて" (바나나의 칼로리를 알려줘), 또는 "今日の朝食にバナナを1本記録して" (오늘 아침에 바나나 한 개를 기록해줘 — Phase 3/4가 설정되고 검증된 후).

감사의 글

-
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