fatsecret-mcp
fatsecret-mcp
Персональный удалённый MCP-сервер (Model Context Protocol), который позволяет Claude искать в базе продуктов/рецептов FatSecret и читать/записывать ваш личный дневник питания, вес и журнал упражнений прямо в диалоге. Развёрнут на бесплатном тарифе Hobby от Vercel. Родственный проект 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 не требуется; нужны только OAuth 2.0 Client ID/Secret из консоли разработчика FatSecret.Дневник/вес/упражнения/профиль (Фаза 4): реализовано и частично проверено на реальном аккаунте FatSecret —
get_profile,get_food_diaryиget_exercise_diaryтеперь подтверждены вживую;create_exercise_entry,weight.updateиfind_food_by_barcodeостаются непроверенными реконструкциями по принципу «наилучшего предположения» (см. «Что не проверено» ниже для полной разбивки).Скрипт настройки трёхэтапного 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), чтобы стандартные поля OAuth Client ID/Secret от Claude работали как всегда доступный запасной вариант — полное обоснование см. в README fitness-mcp, оно применимо здесь без изменений.
Каждый сбой на этом уровне — неверный/отсутствующий MCP_BEARER_TOKEN, нераспознанный OAuth client_id, неправильный client_secret, некорректный PKCE, запрещённый redirect_uri — логируется и, опционально, вызывает оповещение в реальном времени; см. «Логирование и оповещение о событиях безопасности» ниже.
② этот сервер ↔ FatSecret — здесь всё сложнее, чем в fitness-mcp, потому что сам FatSecret использует две разные версии OAuth для двух разных видов методов API, и обойти это невозможно — так спроектирован API FatSecret, а не выбор, сделанный здесь:
Категория методов FatSecret | Примеры методов | Как этот сервер аутентифицируется |
Подписанный запрос (без участия конкретного пользователя) |
| OAuth 2.0 Client Credentials — |
Подписанный и делегированный запрос (чтение/запись вашего аккаунта FatSecret) |
| OAuth 1.0a, трёхэтапный, с подписью 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) вstderrчерезconsole.error— настройка не требуется, и на Vercel это отображается в логах функций развёртывания как есть. Фактическое значение bearer-токена / client secret / PKCE verifier никогда не включается — только метаданные о неудачной попытке, поскольку механизм обнаружения, который сам мог бы утечь секрет, за которым он следит, лишён смысла;lib/securityAlert.test.tsиlib/auth.test.tsпроверяют это напрямую.Опциональное оповещение в реальном времени. Если задан
SECURITY_ALERT_WEBHOOK_URL(URL «входящего вебхука» Slack или Discord), то же событие также отправляется туда POST-запросом как однострочное сообщение, так что попытка вторжения всплывает как push-уведомление, а не видна только тогда, когда кто-то случайно откроет просмотрщик логов Vercel. Сбой доставки вебхука (просроченный URL, сетевая ошибка) сам логируется какsecurity_alert_delivery_failed, чтобы молча сломанный вебхук не читался как «попыток не было».
POST вебхука планируется через after() от Next, чтобы он выполнялся после того, как ответ уже отправлен (без дополнительной задержки на проверку аутентификации); это работает только внутри реального запроса, поэтому при прямом вызове (например, из тестов) используется обычный вызов «запустил и забыл».
Это намеренно простая схема «оповещать о каждом сбое», а не пороговая/частотная — см. комментарии в lib/auth.ts/lib/securityAlert.ts о том, что было исключено из объёма (пороги на основе счётчиков, мониторинг на уровне платформы Vercel, ротация учётных данных) и почему.
Доступные инструменты
Инструмент | Тип | Требуемая аутентификация | Описание |
| чтение | OAuth2 (приложение) | Поиск по базе продуктов FatSecret по названию |
| чтение | OAuth2 (приложение) | Полная пищевая ценность на порцию для одного продукта |
| чтение | 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 может вызвать любой инструмент.
Что не проверено
На момент первой разработки этого проекта регистрации в API FatSecret не существовало, поэтому большая часть начиналась как реконструкции по принципу «наилучшего предположения». С тех пор часть инструментов была проверена на реальном аккаунте — статус ниже:
Подтверждено вживую, точно соответствует реализации:
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) id/дата, как у записейfood_entries.get— не проверено.Всё ещё не проверено / реконструкции по принципу «наилучшего усилия»: форма ответа
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 выше.Проверьте, какие области (scopes) включает ваш тариф (
basic/premier/barcode/ ...) — сообщается, чтоweights.get_month/weight.update/find_food_by_barcodeтребуют Premier или областейbarcode/premier; сверьтесь со своим тарифом и при необходимости скорректируйтеFATSECRET_OAUTH2_SCOPE.Внесите в белый список ваш исходящий IP-адрес(а) (до 15 адресов/диапазонов) — ограничение по IP в FatSecret не ограничивается только конечной точкой токена: подтверждено на реальном развёртывании Vercel, что сам вызов API
foods.searchбыл отклонён (код ошибки 21, «Обнаружен недопустимый IP-адрес») с IP, не входящего в белый список, даже при действительном выпущенном токене. Таким образом, и одноразовое получение токена OAuth2, и каждый отдельный вызов поиска/детализации должны исходить с IP из белого списка. Локально это просто публичный IP вашей машины (curl https://ifconfig.me). На Vercel, где серверless-функции по умолчанию не имеют фиксированного исходящего IP, см. «Фиксированный исходящий IP для Vercel» ниже — это требуется до того, как любой инструмент Signed Request заработает в продакшене.
Один раз запустите локальный dev-сервер для быстрой проверки поиска (для фазы 2 нужен только шаг 1):
npm install cp .env.example .env.local # fill in FATSECRET_CLIENT_ID/SECRET + the MCP_BEARER_TOKEN/OAuth trio vercel devВыполните одноразовую настройку трёхэтапного OAuth1 (нужна для всех инструментов, кроме 5 поисковых/детальных) — см. Фазу 3 ниже.
Разверните на Vercel — см. «Развёртывание» ниже, но сначала прочтите «Фиксированный исходящий IP для Vercel».
Фиксированный исходящий IP для Vercel
Серверless-функции Vercel не имеют фиксированного исходящего IP, что является проблемой с учётом приведённого выше вывода — каждый вызов search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode, а не только получение токена, должен исходить с IP из белого списка. Без этого эти пять инструментов отлично работают локально (ваш IP — это то, что вы внесли в белый список), но в продакшене завершаются ошибкой FatSecret API error 21: Invalid IP address detected.
Решение: маршрутизируйте эти запросы через HTTP-прокси с фиксированным IP. Этот сервер поддерживает Fixie из коробки:
Зарегистрируйтесь на usefixie.com — бесплатного тарифа
tricycleFree(500 запросов/100 МБ в месяц, $0) достаточно для личного использования, поскольку он несёт только трафик Signed Request FatSecret, а не всё ваше приложение. Учтите, что квота запросов тарифа — реальное ограничение, в отличие от лимита скорости только для приложения — если вы много ищете, следите за использованием и переходите на более высокий тариф (commuter, $5/мес/2500 запросов), если приблизитесь к лимиту.Скопируйте URL прокси, который даёт Fixie (
http://fixie:<password>@<host>:<port>).Установите его как
FIXIE_URL— в.env.localдля локального тестирования через прокси и как переменную окружения Vercel для продакшена. Оставьте его неустановленным для обычной локальной разработки (где ваш собственный IP уже напрямую внесён в белый список) —lib/fatsecret/appAuth.tsмаршрутизирует через прокси только при наличииFIXIE_URL.Внесите фиксированный IP Fixie (показан на панели управления Fixie) в консоль разработчика FatSecret, в дополнение к (а не вместо) любым IP, которые вы внесли в белый список для локальной разработки.
Никакой другой трафик сервер→FatSecret не проходит через этот прокси — запросы OAuth1 (Signed & Delegated) в lib/fatsecret/oauth1.ts не ограничены по 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: одноразовая настройка трёхэтапного OAuth1
Каждому инструменту, кроме search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode, нужен токен доступа/секрет OAuth1, привязанный к вашему аккаунту FatSecret. Получите его один раз:
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 никогда не развёртывается) — см. «Развёртывание» ниже.
Согласно документации FatSecret, этот токен доступа не истекает. Если он когда-либо будет отозван (например, вы удалите доступ приложения в настройках аккаунта FatSecret), просто повторно запустите скрипт, чтобы получить новый — следуйте духу паттерна derive() из fitness-mcp: потеря учётных данных здесь не катастрофа, это исправление одной командой, просто на этот раз интерактивной, а не детерминированным повторным выводом.
Генерация секретов для 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-токена, подпись кода OAuth2.1/PKCE/разрешение redirect-URI (включая тестовый вектор RFC 7636), получение/кэширование/обновление токена OAuth2 Client Credentials FatSecret (lib/fatsecret/appAuth.test.ts), подпись HMAC-SHA1 OAuth1, перекрёстно проверенная с независимой реализацией (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; маршруты метаданных OAuth.well-known.E2E (
test/e2e/*.e2e.test.mjs): запускает продакшен-сборку и проверяет по реальному HTTP — проверка работоспособности, 401 при неверной/отсутствующей аутентификации,tools/listвозвращает все 17 инструментов, метаданные обнаружения OAuth и полный цикл авторизационного кода + PKCE. Не обращается к реальным данным FatSecret (в CI по замыслу нет реальных учётных данных).
Ручная проверка на реальном аккаунте FatSecret
CI никогда не касается реальных данных FatSecret, и — согласно разделу «Что не проверено» выше — некоторые предположения этого сервера о точных формах ответов FatSecret вообще не проверялись на реальном аккаунте. После регистрации и запуска скрипта настройки OAuth1 пройдитесь по этому чек-листу и исправьте любые несоответствия, которые найдёте:
Установите реальныеFATSECRET_CLIENT_ID/FATSECRET_CLIENT_SECRETв.env.local, запуститеvercel devи вызовитеsearch_foodsс реальным запросом— готово, подтверждено работающим на реальном аккаунте. Всё же сделайте это дляget_food_detail, если ещё не сделали — убедитесь, что он возвращает разумные значения питательных веществ.Вызовите
search_recipesиget_recipe_detailаналогично. Всё ещё открыто.Если ваш тариф включает область
barcode, вызовитеfind_food_by_barcodeсо штрих-кодом реального продукта и подтвердите, что форма ответа соответствуетRawFindIdForBarcodeResponseизlib/fatsecret/foods.ts— исправьте, если нет. Всё ещё открыто.Запуститеnpm run fatsecret:oauth-setup, затем вызовитеget_profileи— готово.get_food_diaryget_food_diaryсовпал точно; вget_profileотсутствовалheightCm, теперь исправлено — см. «Что не проверено» выше.Вызовите
create_food_diary_entryсconfirm: trueи заведомо одноразовой записью, затемget_food_diaryза ту же дату и подтвердите, что запись появляется с правильной едой/порцией/количеством/приёмом пищи. Затем обновите её черезupdate_food_diary_entryи удалите черезdelete_food_diary_entry— подтвердите, что каждый цикл работает. Всё ещё открыто — обратите внимание, чтоmealвозвращается с заглавной буквы ("Breakfast") изget_food_diary; стоит перепроверить, принимает лиcreate_food_diary_entry/update_food_diary_entryтот же регистр при записи (или какой регистр на самом деле ожидает сторона записи FatSecret), прежде чем предполагать, что всё в порядке.Если ваш тариф включает отслеживание веса, вызовите
update_weightсconfirm: trueи подтвердите, что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 Client Credentials — подписывают методы Signed Request (инструменты поиска/детализации) |
| Необязательно. OAuth2-области через пробел, по умолчанию |
| Необязательно. По умолчанию |
| Необязательно. URL фиксированного IP-прокси HTTP ( |
| Consumer Key/Secret OAuth 1.0 — подписывают и одноразовый скрипт настройки, и каждый вызов Signed & Delegated |
| Токен доступа/секрет OAuth 1.0 для вашего аккаунта FatSecret — получаются через |
| Общий секрет, который этот сервер требует при каждом запросе, а также access_token, выдаваемый нашим OAuth-процессом |
| Учётные данные для собственного минимального OAuth-сервера авторизации этого сервера |
| Необязательно. Разрешённый список через запятую для |
| Необязательно. URL входящего вебхука Slack/Discord для оповещений в реальном времени о сбоях аутентификации — см. «Логирование событий безопасности и оповещения» выше. Сбои всегда записываются в |
Задайте их в разделе Environment Variables проекта Vercel (Production + Preview). Никогда не коммитьте реальные значения — .env.example только документирует названия.
Развёртывание
vercel linkvercel env add FATSECRET_CLIENT_ID(повторите для каждой переменной из таблицы выше, для которой у вас есть значение — как минимумFATSECRET_CLIENT_ID/SECRET,MCP_BEARER_TOKEN,OAUTH_CLIENT_ID/SECRET; добавьтеFIXIE_URLсогласно разделу «Фиксированный исходящий IP для Vercel» выше — на практике это обязательно, а не опционально; добавьте паруFATSECRET_CONSUMER_*/FATSECRET_ACCESS_TOKEN*, как только запустите скрипт настройки OAuth1)Установите версию Node.js проекта Vercel на 22.19 или новее (Project → Settings → General → Node.js Version или там, где это сейчас находится в панели Vercel) до развёртывания — то есть до шага 4 ниже. Зависимость
undici@8этого сервера (используется для прокси Fixie — см. «Фиксированный исходящий IP для Vercel» выше) объявляет"engines": {"node": ">=22.19.0"}, и собственное полеenginesвpackage.jsonздесь документирует то же требование — но ни то, ни другое само по себе ничего не принуждает на Vercel, поэтому проект, всё ещё закреплённый за старой версией Node (например, 20.x), развернётся «успешно», а затем упадёт во время выполнения.Подключите этот GitHub-репозиторий в панели Vercel для автоматического развёртывания при пуше в
main, либо запуститеvercel --prodвручную.Запишите URL развёртывания (проверьте Project → Settings → Domains — продакшен-URL этого проекта оказался незанятым
https://fatsecret-mcp.vercel.app, но это общее пространство имён Vercel, так что не рассчитывайте, что он будет свободен для форка).Внесите фиксированный IP Fixie в белый список в консоли разработчика FatSecret (см. «Фиксированный исходящий IP для Vercel» выше) — это шаг, который с наибольшей вероятностью подведёт в продакшене, поскольку без него
search_foods/get_food_detail/search_recipes/get_recipe_detail/find_food_by_barcode— все завершаются ошибкойFatSecret API error 21.
Подключение к Claude
Пользовательские коннекторы можно добавить только из claude.ai (веб) или десктопного приложения — не из мобильного. После добавления они автоматически доступны и на мобильных устройствах.
На claude.ai: Settings → Connectors → Add custom connector.
Название:
FatSecret. URL:https://<your-deployment>/api/mcp.Если в вашем аккаунте есть бета-функция «Request headers»: добавьте туда
Authorization: Bearer <MCP_BEARER_TOKEN>и переходите к шагу 5.В противном случае откройте Advanced settings и заполните OAuth Client ID / OAuth Client Secret значениями
OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET, заданными в Vercel. Claude автоматически обнаружит конечные точки/authorizeи/tokenчерез метаданные.well-knownэтого сервера.Сохраните. Claude должен показать 17 инструментов, перечисленных выше.
Попробуйте спросить: «バナナのカロリーを教えて» (скажи, сколько калорий в банане) или «今日の朝食にバナナを1本記録して» (запиши один банан на завтрак сегодня — после того как этапы 3/4 настроены и проверены).
Благодарности
На дизайн трёхэтапного OAuth1-процесса повлиял fcoury/fatsecret-mcp (MIT), который предоставляет OAuth-процесс в виде самих MCP-инструментов; этот проект вместо этого запускает его один раз как отдельный скрипт настройки (scripts/fatsecret-oauth-setup.ts), поскольку он рассчитан на один личный аккаунт FatSecret, а не на многопользовательское использование. Код из него не копировался.
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