Skip to main content
Glama
jcrispiniano

huckleberry-mcp-worker

by jcrispiniano

huckleberry-mcp-worker

Сервер Model Context Protocol для приложения отслеживания младенцев Huckleberry, работающий как Cloudflare Worker.

Это порт TypeScript для bckenstler/py-huckleberry-mcp. Оригинал — это Python-сервер stdio, который общается с Firestore через google-cloud-firestore, использующий gRPC и поэтому не работающий на Workers. Этот порт обращается к тому же бэкенду через Firebase REST API, используя fetch, и предоставляет MCP через Streamable HTTP.

Практическое отличие: он всегда включён. Ноутбуку не нужно быть включённым, чтобы клиент мог записать сон.

Инструменты

Все 23 инструмента из Python-сервера реализованы, плюс delete_record.

Область

Инструменты

Дети

list_children, get_child_name

Сон

log_sleep, start_sleep, pause_sleep, resume_sleep, complete_sleep, cancel_sleep, get_sleep_history

Кормление

log_breastfeeding, log_bottle_feeding, start_breastfeeding, pause_feeding, resume_feeding, switch_feeding_side, complete_feeding, cancel_feeding, get_feeding_history

Подгузник

log_diaper, get_diaper_history

Рост

log_growth, get_latest_growth, get_growth_history

Записи

delete_record

Каждый инструмент истории сообщает interval_id каждой записи, который принимает delete_record.

Related MCP server: remote-mcp-authless

Исправления по сравнению с Python-сервером

При портировании были обнаружены и исправлены четыре дефекта.

Длительность грудного вскармливания записывалась в неправильных единицах. Бэкенд хранит leftDuration / rightDuration в секундах — именно так пишет собственный таймер приложения, — но log_breastfeeding передавал минуты вызывающего напрямую. Запись 5-минутного кормления записывала 5 секунд. Ранее вызывающие должны были передавать 300, чтобы обозначить 5 минут; здесь left_duration_minutes: 5 означает пять минут.

Запросы истории за один день ничего не возвращали. Оба конца диапазона дат разрешались в полночь, поэтому start_date == end_date давал пустое окно, и сервер не сообщал о записях за день, в котором их было много. Диапазоны теперь полуоткрытые [start_of_start_date, start_of_end_date + 1 day), что делает оба конца включительными.

end_time в истории сна всегда было null. Код читал поле end, которое бэкенд никогда не записывает. Теперь оно выводится из start + duration.

birth_date в list_children всегда было null. Поле бэкенда — birthdate; сервер читал birthDate.

get_feeding_history теперь также возвращает mode каждой записи, а также связанные с ним детали: количество и тип для бутылочек, названия продуктов и реакции для твёрдой пищи. Без них запись о твёрдой пище — пустая строка, неотличимая от сеанса кормления нулевой длины — именно так хорошая запись ошибочно принимается за отсутствующую.

Настройка

Требуется Node 18+ и аккаунт Cloudflare.

npm install
npx wrangler login

Установите секреты — они хранятся зашифрованными Cloudflare и никогда не находятся в репозитории:

npx wrangler secret put HUCKLEBERRY_EMAIL
npx wrangler secret put HUCKLEBERRY_PASSWORD
npx wrangler secret put MCP_AUTH_TOKEN     # a long random string you generate
npx wrangler secret put HUCKLEBERRY_TIMEZONE   # e.g. America/Sao_Paulo

HUCKLEBERRY_TIMEZONE по умолчанию равен America/New_York. Он определяет, как интерпретируются наивные даты и время, такие как "2026-08-17T15:47:00", поэтому его правильная настройка важна.

Развёртывание:

npm run deploy

Аутентификация

URL Worker является общедоступным, и сервер хранит учётные данные к медицинской карте ребёнка, поэтому каждый запрос должен содержать bearer-токен:

Authorization: Bearer <MCP_AUTH_TOKEN>

Запросы без действительного токена получают 401 до любого вызова Huckleberry. Сгенерируйте токен с помощью чего-то вроде openssl rand -base64 32.

Клиенты, которые не могут отправлять заголовки

Некоторые MCP-клиенты принимают только URL — собственные коннекторы claude.ai, например, принимают URL и необязательные учётные данные OAuth без поля для Authorization. Для них сервер также принимает токен как последний сегмент пути:

POST https://<your-worker>.workers.dev/mcp/<MCP_URL_TOKEN>

MCP_URL_TOKEN — это отдельный секрет от MCP_AUTH_TOKEN, и намеренно: пути запросов попадают в журналы доступа, историю браузера и рефереры так, как заголовки — нет. Их разделение означает, что утечка через URL не компрометирует учётные данные заголовка, и любой из них может быть заменён отдельно. Если MCP_URL_TOKEN не задан, маршрут возвращается к MCP_AUTH_TOKEN, что удобно, но отказывается от этого разделения.

npx wrangler secret put MCP_URL_TOKEN

Предпочитайте маршрут с заголовком везде, где клиент его поддерживает.

Конфигурация клиента

Для Claude Code:

claude mcp add --transport http huckleberry https://<your-worker>.workers.dev/mcp \
  --header "Authorization: Bearer <MCP_AUTH_TOKEN>"

Локальная разработка

cp .dev.vars.example .dev.vars   # then fill it in; .dev.vars is gitignored
npm run dev
curl -X POST http://localhost:8787/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Заметки по дизайну

Без состояния. Каждый запрос создаёт новый McpServer через createMcpHandler из SDK agents Cloudflare. Никаких Durable Objects и хранилищ сессий не используется, потому что каждый инструмент — это самодостаточное чтение или запись.

Кэширование токенов. Токены Firebase ID действуют один час и кэшируются в области модуля, поэтому запросы, попадающие в тёплый изолят, пропускают повторную аутентификацию. Холодный изолят стоит одного дополнительного круга. Токен, отклонённый в процессе, вызывает одну повторную аутентификацию и повторную попытку.

Числовые типы. Firestore различает целые числа и числа с плавающей точкой, и приложение записывает одни поля как одно, а другие как другое. Значения, которые должны храниться как числа с плавающей точкой, оборачиваются в dbl(), чтобы записи, созданные здесь, соответствовали записям, созданным приложением.

Мульти-записные документы. История существует в двух формах: обычные документы с полем start верхнего уровня и пакетные документы, содержащие множество записей под полем data. Вложенные starts не могут быть отфильтрованы на стороне сервера, поэтому пакетные документы загружаются целиком и фильтруются в Worker. Записи сообщают, из какой формы они пришли, через поле is_multi_entry.

Удаление записей

У Python-сервера не было удаления, и общепринятое мнение заключалось в том, что бэкенд его не позволяет. Он позволяет: DELETE по пути документа возвращает 200. На самом деле отсутствовал идентификатор записи, который инструменты истории никогда не возвращали.

Теперь инструменты истории возвращают interval_id, и delete_record удаляет указанную запись. Пакетные записи (несколько записей, упакованных в один документ под data) адресуются как <documentId>#<entryKey> и удаляются как поле их родителя.

Удаление также перенаправляет prefs.last* на самую новую выжившую запись. Приложение читает эти указатели напрямую, поэтому удаление без перенаправления оставляет его показывающим запись, которой больше не существует.

Известные ограничения

  • Удаление необратимо. Нет функции отмены. Подтвердите запросом истории перед вызовом delete_record.

  • Твёрдая пища только для чтения. get_feeding_history сообщает записи о твёрдой пище с названиями продуктов и реакциями, но нет инструмента для её создания.

  • start_sleep не защищает от уже запущенного таймера. Python-сервер документировал, что в этом случае произойдёт ошибка, но никогда не проверял; поведение сохранено здесь, а не молча изменено.

  • Заметки не возвращаются в записи сна. Поле details — это фиксированная структура флажков, а не свободный текст.

Лицензия

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    A
    maintenance
    Huckleberry MCP server for Claude, Cursor, and other AI assistants. Query and log baby sleep, feeds, diapers, growth, pumping, and solids.
    29
    21 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for UploadThing that lets AI assistants upload, list, and delete files on UploadThing's CDN via natural language. Runs as a Cloudflare Worker for always-on serverless access.
    16 npm
    MIT