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.

Исправления по сравнению с 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

-
license - not tested
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all MCP Connectors

Latest Blog Posts

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/jcrispiniano/huckleberry-mcp-worker'

If you have feedback or need assistance with the MCP directory API, please join our Discord server