fatsecret-mcp
fatsecret-mcp
개인 원격 MCP(Model Context Protocol) 서버로, Claude가 FatSecret의 음식/레시피 데이터베이스를 검색하고 대화 중에 직접 자신의 음식 일기, 체중, 운동 기록을 읽고 쓸 수 있게 해줍니다. Vercel의 무료 Hobby 티어에 배포됩니다. fitness-mcp(Hevy)의 자매 프로젝트로, 제품당 하나의 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 계정으로 검증되지 않음 — 이 프로젝트를 만들 당시 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 (특정 사용자 미관여) |
| OAuth 2.0 Client Credentials — |
Signed & Delegated Request (당신의 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(설정 스크립트를 한 번 실행하여 획득)이 필요합니다.
노출되는 도구
도구 | 유형 | 필요한 인증 | 설명 |
| 읽기 | 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 (사용자) | 운동 항목 기록 |
쓰기 도구는 기본적으로 드라이런입니다
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의 단위 테스트도 그에 맞게 업데이트해야 합니다).
설정
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단계는 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에 배포하세요 — 아래 배포를 참조하세요.
로컬 개발
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을 출력합니다 — URL을 열고 FatSecret에 로그인한 뒤 승인합니다. FatSecret이 확인 코드를 보여줍니다.
그 코드를 붙여넣도록 요청한 다음, 이를 영구 액세스 토큰/시크릿으로 교환합니다.
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-knownOAuth 메타데이터 라우트도 포함합니다.E2E(
test/e2e/*.e2e.test.mjs): 프로덕션 빌드를 부팅하고 실제 HTTP로 검증합니다 — 상태 확인, 잘못되었거나 누락된 인증 시 401,tools/list가 17개 도구를 모두 반환하는지, OAuth 검색 메타데이터, 그리고 전체 authorization-code + PKCE 라운드 트립. 실제 FatSecret 데이터는 사용하지 않습니다(CI에는 설계상 실제 자격 증명이 없습니다).
실제 FatSecret 계정으로 수동 검증
CI는 실제 FatSecret 데이터를 전혀 건드리지 않으며, 위의 "검증되지 않은 사항"에 따라 이 서버가 FatSecret의 정확한 응답 형태에 대해 가진 일부 가정은 실제 계정으로 전혀 확인되지 않았습니다. 등록 후 OAuth1 설정 스크립트를 실행하고, 이 체크리스트를 진행하면서 발견한 불일치를 수정하세요:
.env.local에 실제FATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRET을 설정하고,vercel dev를 실행한 다음, 실제 쿼리로search_foods를 호출합니다(예: 위의 스모크 테스트curl패턴을tools/call과 함께 사용). 실제 결과가 반환되는지 확인하고 그중 하나에 대해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를 호출해서lib/fatsecret/profile.ts/lib/fatsecret/diary.ts의 필드 이름이 실제 응답과 일치하는지 확인하세요(문서에서 재구성한 것이지 캡처한 것이 아닙니다).confirm: true와 함께 명백히 임시로 쓸 항목으로create_food_diary_entry를 호출한 다음, 같은 날짜에 대해get_food_diary를 호출하여 해당 항목이 올바른 음식/섭취량/수량/식사로 표시되는지 확인하세요. 그런 다음update_food_diary_entry로 수정하고delete_food_diary_entry로 삭제하세요. 각각이 정상적으로 왕복되는지 확인하세요.요금제에 체중 추적이 포함된 경우
confirm: true와 함께update_weight를 호출하고get_weight_history가 이를 반영하는지 확인하세요.create_exercise_entry와get_exercise_diary는 이 코드베이스에서 가장 검증이 덜 된 쌍입니다(lib/fatsecret/exercise.ts상단의 경고 참조). 이 기능에 의존하기 전에 https://platform.fatsecret.com/docs/guides에서 정확한 메서드 이름/매개변수를 확인하세요. 이 경우 단순 검증이 아니라 실제 수정이 필요할 수 있습니다.실제 FatSecret 자격 증명을 커밋하지 말고, 이 체크리스트를 CI에서 실행하지 마세요.
환경 변수
변수 | 목적 |
| OAuth 2.0 Client Credentials — Signed Request 메서드(검색/상세 도구) |
| 선택 사항. 공백으로 구분된 OAuth2 범위, 기본값 |
| 선택 사항. 기본값 |
| OAuth 1.0 Consumer Key/Secret — 일회성 설정 스크립트와 모든 Signed & Delegated 호출에 서명 |
| OAuth 1.0 access token/secret — 사용자 본인의 FatSecret 계정용. |
| 이 서버가 모든 요청에 요구하는 공유 비밀값이며, 이 서버의 OAuth 흐름이 발급하는 access_token |
| 이 서버 자체의 최소 OAuth 인증 서버용 자격 증명 |
| 선택 사항. |
이 값들을 Vercel 프로젝트의 환경 변수(Production + Preview)에 설정하세요. 실제 값을 커밋하지 마세요 — .env.example은 이름만 문서화합니다.
배포
vercel linkvercel env add FATSECRET_CLIENT_ID(위 표에서 값이 있는 모든 변수에 대해 반복 — 최소한FATSECRET_CLIENT_ID/SECRET,MCP_BEARER_TOKEN,OAUTH_CLIENT_ID/SECRET; OAuth1 설정 스크립트를 실행한 후에는FATSECRET_CONSUMER_*/FATSECRET_ACCESS_TOKEN*쌍도 추가)Vercel 대시보드에서 이 GitHub 저장소를 연결하여
main에 푸시할 때 자동 배포되게 하거나, 수동으로vercel --prod를 실행하세요.배포된 URL을 확인하세요(프로젝트 → 설정 → 도메인을 확인.
fatsecret-mcp.vercel.app은 Vercel의 공유 네임스페이스에서 이미 사용 중일 수 있습니다).해당 배포의 아웃바운드 IP를 허용 목록에 추가하세요. FatSecret 개발자 콘솔에서 OAuth2 토큰 요청용으로(설정 1단계 참조) — Vercel 서버리스 함수는 기본적으로 고정 IP가 없으므로 이 단계가 프로덕션에서 가장 문제가 될 가능성이 높습니다.
Claude에 연결
사용자 지정 커넥터는 claude.ai(웹) 또는 데스크톱 앱에서만 추가할 수 있습니다. 모바일 앱에서는 추가할 수 없습니다. 일단 추가되면 모바일에서 자동으로 사용할 수 있습니다.
claude.ai에서: 설정 → 커넥터 → 사용자 지정 커넥터 추가.
이름:
FatSecret. URL:https://<your-deployment>/api/mcp.계정에 "Request headers" 베타 기능이 있다면: 거기에
Authorization: Bearer <MCP_BEARER_TOKEN>을 추가하고 5단계로 건너뛰세요.그렇지 않으면 고급 설정을 열고 Vercel에 설정된
OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET값으로 OAuth Client ID / OAuth Client Secret을 입력하세요. Claude는 이 서버의.well-known메타데이터를 통해/authorize및/token엔드포인트를 자동으로 발견합니다.저장하세요. Claude는 위의 17개 도구를 나열해야 합니다.
다음과 같이 물어보세요: "バナナのカロリーを教えて" (바나나의 칼로리를 알려줘), 또는 "今日の朝食にバナナを1本記録して" (오늘 아침에 바나나 한 개를 기록해줘 — Phase 3/4가 설정되고 검증된 후).
감사의 글
This server cannot be installed
Maintenance
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
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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