fatsecret-mcp
fatsecret-mcp
Claude가 대화 중에 FatSecret의 음식/레시피 데이터베이스를 검색하고 자신의 음식 일기, 체중, 운동 기록을 읽고 쓸 수 있게 해주는 개인 원격 MCP(Model Context Protocol) 서버입니다. Vercel의 무료 Hobby 요금제에 배포됩니다. fitness-mcp(Hevy)의 자매 프로젝트로, 제품당 하나의 MCP 서버를 두고 동일한 인증 패턴을 공유합니다.
라이선스
Related MCP server: Nutrition MCP
상태
검색(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 계정으로 부분 검증 완료 —
get_profile,get_food_diary,get_exercise_diary는 이제 실사용 확인이 끝났습니다.create_exercise_entry,weight.update,find_food_by_barcode는 여전히 미검증된 최선의 재구성(best-effort reconstruction)입니다(전체 내역은 아래 "검증되지 않은 항목" 참조).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의 static-header 옵션이 아직 베타 게이트에 있기 때문에, 이 서버는 자체 최소 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 메서드 카테고리 | 예시 메서드 | 이 서버의 인증 방식 |
서명된 요청(특정 사용자 미포함) |
| OAuth 2.0 Client Credentials — |
서명 및 위임된 요청(사용자의 FatSecret 계정 읽기/쓰기) |
| OAuth 1.0a, 3-legged, HMAC-SHA1 서명 — |
구체적으로: 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(설정 스크립트를 한 번 실행하여 획득)이 필요합니다.
보안 이벤트 로깅 및 알림
위 ① 계층(Claude ↔ 이 서버)의 모든 실패한 검사는 lib/securityAlert.ts를 통해 보고되며, 다음 지점을 게이트합니다:
lib/auth.ts(verifyBearerToken) — bearer 토큰 누락, 잘못된 bearer 토큰,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미설정.
두 개의 독립적인 계층으로 구성되어 있어 우아하게 저하됩니다:
항상 로깅. 위의 모든 실패는 구조화된 JSON 한 줄(
event,reason,ip,userAgent,path,time)을console.error를 통해stderr에 기록합니다 — 설정이 필요 없으며, Vercel에서는 배포의 함수 로그에 그대로 표시됩니다. 실제 bearer 토큰 / client secret / PKCE verifier 값은 절대 포함되지 않습니다 — 감시 중인 비밀키를 스스로 유출할 수 있는 탐지 메커니즘은 의미가 없기 때문입니다.lib/securityAlert.test.ts와lib/auth.test.ts가 이를 직접 검증합니다.선택적 실시간 알림.
SECURITY_ALERT_WEBHOOK_URL이 설정된 경우(Slack 또는 Discord "incoming webhook" URL), 동일한 이벤트가 한 줄 메시지로 해당 URL에도 POST되어, 침입 시도가 누군가 Vercel 로그 뷰어를 열었을 때만 보이는 것이 아니라 푸시 알림으로 표면화됩니다. 웹훅 전달 실패(만료된 URL, 네트워크 오류)는 그 자체로security_alert_delivery_failed로 기록되므로, 조용히 고장난 웹훅이 "시도 없음"으로 읽히지 않습니다.
웹훅 POST는 Next의 after()를 통해 예약되어 응답이 이미 전송된 후에 실행됩니다(인증 검사에 지연 시간이 추가되지 않음). 이는 실제 요청 내에서만 작동하므로, 직접 호출될 때(예: 테스트에서)는 일반 fire-and-forget 호출로 폴백합니다.
의도적으로 "모든 실패 시 알림"이라는 단순한 설계이며, 임계값/비율 기반 알림이 아닙니다 — 범위에서 제외된 항목(카운트 기반 임계값, Vercel의 자체 플랫폼 수준 모니터링, 자격 증명 순환)과 그 이유는 lib/auth.ts/lib/securityAlert.ts의 문서 주석을 참조하세요.
노출된 도구
도구 | 유형 | 필요한 인증 | 설명 |
| 읽기 | OAuth2(앱) | 이름으로 FatSecret의 음식 데이터베이스 검색 |
| 읽기 | OAuth2(앱) | 한 음식의 1회 제공량별 전체 영양 정보 |
| 읽기 | OAuth2(앱) | FatSecret의 레시피 데이터베이스 검색 |
| 읽기 | OAuth2(앱) | 한 레시피의 전체 재료/조리법 |
| 읽기 | OAuth2(앱) | GTIN-13 바코드를 foodId로 변환 — |
| 읽기 | OAuth1(사용자) | 특정 날짜의 음식 일기 항목 목록 |
| 읽기 | OAuth1(사용자) | 즐겨찾기한 음식 목록 |
| 읽기 | OAuth1(사용자) | 가장 많이 먹은 음식 목록, 선택적으로 식사별 |
| 읽기 | OAuth1(사용자) | 최근에 먹은 음식 목록, 선택적으로 식사별 |
| 읽기 | OAuth1(사용자) | 한 달의 체중 항목 목록 — Premier 전용일 수 있음 |
| 읽기 | OAuth1(사용자) | 특정 날짜의 운동 항목 목록 |
| 읽기 | OAuth1(사용자) | 사용자의 FatSecret 프로필 요약 가져오기 |
| 쓰기 | OAuth1(사용자) | 음식을 일기에 기록 |
| 쓰기 | OAuth1(사용자) | 기존 일기 항목 업데이트 |
| 쓰기 | OAuth1(사용자) | 일기 항목 삭제 |
| 쓰기 | OAuth1(사용자) | 체중 항목 기록/업데이트 — Premier 전용일 수 있음 |
| 쓰기 | OAuth1(사용자) | 운동 항목 기록 |
쓰기 도구는 기본적으로 dry-run
fitness-mcp와 동일한 설계: 모든 쓰기 도구는 confirm: true 인자가 필요합니다. 도구 설명은 호출하는 LLM에게 사용자에게 정확히 무엇이 기록될지 보여주고 먼저 명시적인 동의를 받도록 지시합니다. 이는 구조적 유도일 뿐 보장은 아닙니다 — 도구를 호출할지 결정하는 동일한 LLM이 confirm도 설정하며, 인증 계층에서 읽기/쓰기 도구 간의 범위 분리가 없으므로 유효한 MCP_BEARER_TOKEN을 가진 모든 호출자가 어떤 도구든 호출할 수 있습니다.
검증되지 않은 항목
이 프로젝트를 처음 만들 때 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의 단위 테스트도 그에 맞게 업데이트해야 함).
설정
FatSecret Platform API 앱을 등록하세요: https://platform.fatsecret.com/. 다음을 받게 됩니다:
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를 조정하세요.아웃바운드 IP를 허용 목록에 추가하세요(최대 15개 주소/범위) — FatSecret의 IP 제한은 토큰 엔드포인트에만 국한되지 않음: 실제 Vercel 배포에서 확인한 결과, 유효하게 발급된 토큰이 있어도 허용 목록에 없는 IP에서 실제
foods.searchAPI 호출 자체가 거부되었음(오류 코드 21, "Invalid IP address detected"). 따라서 일회성 OAuth2 토큰 가져오기와 모든 검색/상세 호출 각각이 허용 목록에 있는 IP에서 시작되어야 함. 로컬에서는 이는 단순히 사용자 머신의 공용 IP(curl https://ifconfig.me)입니다. 기본적으로 고정 아웃바운드 IP가 없는 서버리스 함수를 사용하는 Vercel에서는 아래의 "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일회성 3-legged OAuth1 설정을 실행하세요(5개의 검색/상세 도구를 제외한 모든 도구에 필요) — 아래 3단계 참조.
Vercel에 배포하세요 — 아래 Deploy를 참조하되, 먼저 "Vercel용 고정 아웃바운드 IP"를 읽으세요.
Vercel용 고정 아웃바운드 IP
Vercel의 서버리스 함수는 고정 아웃바운드 IP가 없는데, 위의 발견 사항을 고려하면 이는 문제입니다 — 토큰 가져오기뿐만 아니라 모든 search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode 호출이 허용 목록에 있는 IP에서 시작되어야 합니다. 이것이 없으면 이 다섯 도구는 로컬에서는(허용 목록에 추가한 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이 있을 때만 프록시를 통해 라우팅합니다.Fixie의 고정 IP(Fixie 대시보드에 표시됨)를 FatSecret 개발자 콘솔에서 로컬 개발용으로 허용 목록에 추가한 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개 도구를 반환해야 합니다. 토큰이 없거나 잘못된 요청은 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)는 다음을 수행합니다:
FatSecret에서 승인되지 않은 요청 토큰을 요청합니다.
승인 URL을 출력합니다 — 열어서 FatSecret에 로그인하고 승인하세요. FatSecret이 확인 코드를 표시합니다.
해당 코드를 붙여넣으라는 프롬프트를 표시한 후, 영구 액세스 토큰/시크릿으로 교환합니다.
FATSECRET_ACCESS_TOKEN/FATSECRET_ACCESS_TOKEN_SECRET을.env.local에 기록합니다.
그런 다음 동일한 두 값을 Vercel의 환경 변수에도 추가하세요(.env.local은 배포되지 않음) — 아래 Deploy 참조.
FatSecret 문서에 따르면 이 액세스 토큰은 만료되지 않습니다. 만약 해지되면(예: FatSecret 계정 설정에서 앱의 액세스를 제거한 경우), 새 토큰을 얻기 위해 스크립트를 다시 실행하면 됩니다 — 정신적으로 fitness-mcp의 derive() 패턴을 참고하세요: 여기서 자격 증명을 잃는 것은 재앙이 아니라 한 명령으로 해결되는 문제이며, 이번에는 결정적 재파생 대신 대화형이라는 점만 다릅니다.
기억에 남는 하나의 암호문구에서 Claude-facing 시크릿 생성
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): 베어러 토큰 검증, OAuth2.1 코드 서명/PKCE/리다이렉트 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) 도구 경로를 모두 다루며 모든 쓰기 도구에 확인 게이팅을 적용; 실제/api/oauth/authorize//api/oauth/token라우트;.well-knownOAuth 메타데이터 라우트.E2E(
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_foods를 호출— 완료, 실제 계정으로 작동 확인됨. 아직 안 했다면get_food_detail도 동일하게 수행하세요 — 합리적인 영양 수치를 반환하는지 확인.search_recipes와get_recipe_detail도 유사하게 호출하세요. 여전히 미해결.플랜에
barcode스코프가 포함되어 있다면, 실제 제품의 바코드로find_food_by_barcode를 호출하고 응답 형태가lib/fatsecret/foods.ts의RawFindIdForBarcodeResponse와 일치하는지 확인하세요 — 아니면 수정하세요. 여전히 미해결.npm run fatsecret:oauth-setup을 실행한 후get_profile과get_food_diary를 호출— 완료.get_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 수신 웹훅 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*쌍 추가)배포 전에 Vercel 프로젝트의 Node.js 버전을 22.19 이상으로 설정하세요(프로젝트 → 설정 → 일반 → Node.js 버전, 또는 현재 Vercel 대시보드에서 해당 항목이 있는 위치) — 즉 아래 4단계 이전에. 이 서버의
undici@8의존성(Fixie 프록시에 사용 — 위의 "Vercel용 고정 아웃바운드 IP" 참조)은"engines": {"node": ">=22.19.0"}을 선언하며, 여기package.json의 자체engines필드도 동일한 요구 사항을 문서화합니다 — 하지만 둘 다 Vercel에서 자체적으로 아무것도 강제하지 않으므로, 여전히 이전 Node 버전(예: 20.x)에 고정된 프로젝트는 "성공적으로" 배포된 후 런타임에 실패합니다.main에 푸시할 때 자동 배포를 위해 Vercel 대시보드에서 이 GitHub 저장소를 연결하거나, 수동으로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단계로 건너뛰세요.그렇지 않으면 고급 설정을 열고 OAuth 클라이언트 ID / OAuth 클라이언트 시크릿에 Vercel에 설정된
OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET값을 입력하세요. Claude는 이 서버의.well-known메타데이터를 통해/authorize및/token엔드포인트를 자동으로 발견합니다.저장. Claude는 위의 17개 도구를 나열해야 합니다.
다음과 같이 물어보세요: "バナナのカロリーを教えて"(바나나의 칼로리를 알려줘), 또는 "今日の朝食にバナナを1本記録して"(오늘 아침 식사로 바나나 1개를 기록해줘 — 3/4단계가 설정되고 검증된 후).
감사의 말
3-legged OAuth1 흐름 설계는 fcoury/fatsecret-mcp(MIT)에서 참고했습니다. 해당 프로젝트는 OAuth 흐름을 MCP 도구 자체로 노출하지만, 이 프로젝트는 단일 개인 FatSecret 계정을 위해 구축되었으므로 다중 사용자용이 아닌 일회성 독립 실행형 설정 스크립트(scripts/fatsecret-oauth-setup.ts)로 실행합니다. 코드는 복사되지 않았습니다.
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