Skip to main content
Glama
sevavsegdakotov

Nutrition Hub

README.md
# Nutrition Hub

[![Tests](https://github.com/sevavsegdakotov/nutrition-hub/actions/workflows/tests.yml/badge.svg)](https://github.com/sevavsegdakotov/nutrition-hub/actions/workflows/tests.yml)

Самостоятельно разворачиваемый MCP-сервис, который связывает ChatGPT или другой MCP-клиент с личным дневником FatSecret и Markdown-дневником в Obsidian.

Сервис рассчитан на **одного пользователя и один FatSecret-аккаунт**. Каждый пользователь разворачивает собственный экземпляр и никому не передаёт свои OAuth-токены.

## Возможности

- поиск продуктов в FatSecret;
- поиск ранее использованных локальных продуктов в недавних, избранном и дневнике;
- безопасный черновик блюда до записи;
- явное подтверждение перед изменением дневника;
- защита от повторной записи одного черновика;
- откат при частичном сбое;
- отмена операций, созданных Nutrition Hub;
- дневные Markdown-заметки для Obsidian;
- работа с ChatGPT через частный Secure MCP Tunnel;
- Streamable HTTP MCP endpoint: `/mcp`.

## Как это работает

```text
Фото / голос / текст
        ↓
ChatGPT или другой MCP-клиент
        ↓
Nutrition Hub
        ├── OAuth 1.0a → FatSecret
        └── Markdown → папка Obsidian
```

Изображения обрабатывает клиент с поддержкой зрения, например ChatGPT. Nutrition Hub не принимает и не хранит фотографии: он получает уже выбранные продукты, порции и подтверждение.

## Требования

- Linux, macOS или VPS с Docker и Docker Compose;
- личный аккаунт FatSecret;
- аккаунт разработчика FatSecret и OAuth 1.0 Consumer Key/Secret;
- отдельная папка внутри Obsidian для дневника питания;
- для ChatGPT: Developer mode и либо Secure MCP Tunnel, либо стабильный публичный HTTPS endpoint.

Доступность Developer mode и Secure MCP Tunnel зависит от аккаунта и политики рабочего пространства OpenAI.

## Быстрая установка

```bash
git clone https://github.com/sevavsegdakotov/nutrition-hub.git
cd nutrition-hub
chmod +x setup.sh authorize-fatsecret.sh
./setup.sh
```

Установщик:

1. спросит часовой пояс;
2. попросит абсолютный путь к отдельной папке питания внутри Obsidian;
3. создаст локальный `.env`;
4. запустит OAuth-авторизацию FatSecret;
5. соберёт и запустит контейнер.

Во время OAuth нужно ввести Consumer Key и Consumer Secret, открыть выданную ссылку в браузере, разрешить доступ в своём пользовательском FatSecret-аккаунте и вставить PIN. Секреты сохраняются локально в `secrets/config.json` с ограниченными правами.

Проверка:

```bash
docker compose ps
curl http://127.0.0.1:3781/healthz
```

Ожидаемый endpoint:

```text
http://127.0.0.1:3781/mcp
```

## Ручная настройка

```bash
cp .env.example .env
```

Отредактируйте `.env`:

- `NUTRITION_TIMEZONE` — часовой пояс, например `Europe/Berlin`;
- `OBSIDIAN_NUTRITION_PATH` — абсолютный путь к отдельной папке дневника;
- `PUID` и `PGID` — идентификаторы пользователя, обычно результат `id -u` и `id -g`;
- `BIND_ADDRESS` — оставьте `127.0.0.1` для частного туннеля;
- `MCP_AUTH_TOKEN` — нужен, если endpoint публикуется напрямую и клиент умеет передавать Bearer-заголовок.

Затем:

```bash
mkdir -p data secrets
chmod 700 data secrets
./authorize-fatsecret.sh
docker compose up -d --build
```

## Подключение к ChatGPT

Рекомендуемый вариант — OpenAI Secure MCP Tunnel: сервер остаётся на `127.0.0.1` и не открывает входящий порт в интернет.

1. Создайте туннель в настройках OpenAI Platform.
2. Установите актуальный `tunnel-client` по официальной инструкции OpenAI.
3. Настройте HTTP-профиль на `http://127.0.0.1:3781/mcp`.
4. Выполните `tunnel-client doctor --profile nutrition-hub --explain`.
5. Запустите `tunnel-client run --profile nutrition-hub`.
6. В ChatGPT включите Developer mode.
7. На странице Plugins создайте приложение и в разделе Connection выберите Tunnel.
8. Проверьте, что ChatGPT увидел семь инструментов Nutrition Hub.

Официальные инструкции:

- [Secure MCP Tunnel](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels)
- [Подключение MCP к ChatGPT](https://developers.openai.com/plugins/deploy/connect-chatgpt)

Для публичного подключения нужен стабильный HTTPS endpoint с `/mcp` и подходящей аутентификацией. Встроенный статический Bearer-токен подходит только клиентам, которые умеют передавать заголовок `Authorization`. Для публичного плагина ChatGPT потребуется отдельный поддерживаемый слой аутентификации и процедура публикации OpenAI.

## Инструменты MCP

| Инструмент | Что делает | Изменяет данные |
|---|---|---|
| `search_food` | Ищет продукты в US-каталоге и известных продуктах профиля | Нет |
| `get_food` | Возвращает карточку продукта и варианты порций | Нет |
| `preview_meal` | Создаёт временный черновик на четыре часа | Нет |
| `commit_meal` | Записывает подтверждённый черновик в FatSecret и Obsidian | Да |
| `undo_meal` | Отменяет операцию Nutrition Hub | Да |
| `get_day_status` | Показывает живой дневник и операции за дату | Нет |
| `sync_day_to_obsidian` | Пересобирает управляемый Markdown-блок за дату | Только Obsidian |

## Пример работы

Пользователь:

> Подготовь черновик ужина: курица 150 г и рис 200 г. Ничего не записывай без моего подтверждения.

Клиент вызывает `search_food`, `get_food` и `preview_meal`, затем показывает пользователю продукты, порции, калории и БЖУ.

Только после фразы вроде «Подтверждаю запись» клиент вызывает `commit_meal` с `confirmed=true`.

## Локальные и русскоязычные продукты

Бесплатный FatSecret API использует US dataset. Он может не находить российские продукты, региональные бренды и локальные блюда.

Nutrition Hub использует безопасный обход для продуктов, уже присутствующих в личном профиле:

1. добавьте продукт один раз через официальное мобильное приложение FatSecret;
2. Nutrition Hub найдёт его в `recently_eaten`, `favorite` или дневнике за последние семь дней;
3. продукт можно будет повторять через `source_entry_id` без подстановки неточного американского аналога.

Полный поиск региональной базы требует официального FatSecret Localization-доступа. Проект не использует закрытые мобильные endpoint-ы и не обходит ограничения FatSecret.

## Заметки Obsidian

Сервис пишет только в папку, указанную в `OBSIDIAN_NUTRITION_PATH`. Файл дня имеет вид:

```text
YYYY/YYYY-MM-DD.md
```

Автоматический текст находится между маркерами:

```html
<!-- nutrition-hub:start -->
<!-- nutrition-hub:end -->
```

Личный текст за пределами блока сохраняется при повторной синхронизации.

## Безопасность

- Не коммитьте `.env`, `secrets/` и `data/`.
- Не публикуйте Consumer Secret, пользовательские OAuth-токены и MCP-токены.
- Монтируйте только отдельную папку питания, а не всё хранилище Obsidian.
- Оставляйте `BIND_ADDRESS=127.0.0.1`, если используете Secure MCP Tunnel.
- При прямой сетевой публикации обязательно задайте `MCP_AUTH_TOKEN` и HTTPS.
- Не подключайте к модели сырой `fatsecret-mcp`: в нём есть прямые операции записи и удаления без слоя подтверждения Nutrition Hub.
- Отдельная установка нужна каждому пользователю. Текущая версия не является многопользовательским облачным сервисом.

Подробнее: [SECURITY.md](SECURITY.md).

## Ограничения

- Это не медицинское устройство.
- Распознавание фотографии может ошибаться — вес и состав нужно проверять.
- FatSecret может ограничивать доступность методов, продуктов и регионов по тарифу и стране.
- Сервис не создаёт общую постоянную копию базы FatSecret.
- Импорт официального CSV пока не реализован.
- Автоматическая настройка публичного HTTPS endpoint не входит в первую версию.

## Разработка

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
pytest -q
```

## Происхождение

FatSecret OAuth-клиент и базовый MCP-адаптер основаны на проекте [`madebydia/fatsecret-mcp`](https://github.com/madebydia/fatsecret-mcp), зафиксированном на коммите из файла [UPSTREAM_COMMIT](UPSTREAM_COMMIT). Новая часть проекта добавляет слой подтверждения, идемпотентные операции, откат, безопасную отмену, профильный поиск локальных продуктов и запись в Obsidian.

Проект распространяется по лицензии MIT. Он не связан с FatSecret и не одобрен FatSecret или OpenAI.