Skip to main content
Glama

habitica-mcp

MCP-сервер для самостоятельно размещаемого экземпляра Habitica, работающий поверх Streamable HTTP, чтобы его можно было запускать как обычный сетевой сервис, а не как подпроцесс stdio на одного клиента.

Зачем это существует

Существующий community-сервер (iBreaker/habitica-mcp-server) зашивает https://habitica.com/api/v3, работает только через stdio и не поддерживается уже через три дня после своего создания. Всё это не подходит для самохостируемого экземпляра за ingress-контроллером.

Здесь HABITICA_BASE_URL обязательна и не имеет значения по умолчанию — ошибочное указание на другой экземпляр становится невозможным, а не лишь не рекомендуется.

Related MCP server: habitca-mcp

Инструменты

Инструмент

Примечания

list_tasks

Необязательный фильтр по типу; история исключена (см. ниже)

get_task

create_task

Не идемпотентен — Habitica не имеет ключа идемпотентности

update_task

Частичное обновление

delete_task

Деструктивная операция

score_task

Деструктивное — изменяет золото/опыт/серии и не может быть отменено

list_tags / create_tag

add_tag_to_task / remove_tag_from_task

Принимают имя тега, которое преобразуется в его UUID

get_user_stats

Серверная проекция, а не полный документ пользователя

Конфигурация

Переменная

Обязательность

По умолчанию

Назначение

HABITICA_BASE_URL

да

—

например, http://habitica.tools.svc.cluster.local:3000

HABITICA_USER_ID

да

—

x-api-user

HABITICA_API_TOKEN

да

—

x-api-key

MCP_ALLOWED_HOSTS

нет

(пусто — проверка отключена)

Разрешённый список Host (через запятую) для /mcp

MCP_HOST / MCP_PORT

нет

0.0.0.0 / 8080

HABITICA_TIMEOUT_MS

нет

15000

LOG_LEVEL

нет

info

Эндпоинты: POST/GET/DELETE /mcp и GET /healthz.

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

Четыре важных и неочевидных решения:

Проекция ответа, а не пагинация

GET /tasks/user в Habitica возвращает history: [{date, value}] для каждой привычки и ежедневной задачи — по одной записи на каждое событие начисления очков за всё время жизни аккаунта, и эта опция включена по умолчанию. API не предоставляет limit/offset, поэтому решением является проекция: этот сервер всегда отправляет history=false и дополнительно проецирует каждую задачу на фиксированный набор полей, чтобы изменение схемы на стороне API не могло незаметно внести сотни килобайт в контекст модели. get_user_stats использует ?userFields= по той же причине.

Фильтр списка — во множественном числе и неправильный. GET /tasks/user?type= принимает habits | dailys | todos | rewards | completedTodos (обратите внимание на dailys), тогда как тело запроса создания использует единственное число habit | daily | todo | reward. Инструменты отдают пользователю единственное число и преобразуют его внутри; передача единственного числа в эндпоинт списка даёт ошибку 400.

Проверка Host ограничена только /mcp, никогда не распространяется на всё приложение. createMcpExpressApp применяет её глобально, что сломало бы и пробы kubelet (при httpGet-пробе отправляется Host: <podIP>, а IP-адреса подов нельзя добавить в список разрешённых), и blackbox-мониторинг (он отправляет Host: <твщество>.<namespace>.svc). Поэтому /healthz находится вне проверки; он не раскрывает ничего, а защита от DNS-rebinding важна только для JSON-RPC.

/healthz сообщает только о живости процесса — и никогда о доступности Habitica. Проверка связанности с Habitica превратила бы её перезапуск в CrashLoopBackOff, и тогда liveness-проба продолжала бы убивать процесс, который полностью здоров и просто не можем общаться. Сбои Habitica видны как чистая ошибка JSON-RPC в отдельном инструменте.

Транспорт

Stateless Streamable HTTP (sessionIdGenerator: undefined), построенный на @modelcontextprotocol/server v2 — текущей стабильной основной версии, чей HTTP-транспорт находится в отдельных адаптерах @modelcontextprotocol/express / @modelcontextprotocol/node. Согласованная версия протокола — 2025-11-25 (LATEST_PROTOCOL_VERSION в SDK); v1.x теперь выпускается только с исправлениями безопасности и ошибок.

Новый экземпляр McpServer + транспорт создаётся на каждый запрос и уничтожается по событию close ответа. Создание на каждый запрос обязательно, а не просто для порядка: SDK v1 явно выбрасывает исключение при повторном использовании stateless-транспорта («Stateless transport cannot be reused across requests»), поскольку это вызывает коллизии идентификаторов сообщений между одновременно работающими клиентами.

Стоимость такого подхода реальна и о ней стоит знать: каждый запрос пересобирает ~11 конвертаций zod→JSON-Schema, что составляет примерно 0,5 МБ мусора на вызов. Этот мусор перерабатывается при работе GC (1500 последовательных вызовов стабилизировались на ~193 МиБ при ограничении кучи в 96 МиБ), а не утекают, но именно поэтому при деплое запрашивается больше памяти, чем говорит потребление в известных процессах.

GET /mcp возвращает 405 с Allow: POST. Это допускается спецификацией (сервер может отказать в простоте потоку) и именно этого ожидает MCP-клиент: он обрабатывает 405 как «здесьнет ещё и поток сервера» и останавливается.

Более ранняя версия пыталась пойти навстречу, возвращая пустой SSE-поток. Это привело к бесконечному циклу переподключения: клиент интерпретирует чисто завершённый поток без ответа как потерь соединения и планирует новое подключение, но счётчик повторных попыток увеличивается только при ошибке, потону успешный пустой поток не сбрасывал ничего. Зазмер показал \~1 запрос/сек вечно — 1 → 4 → 8 → 12 GET-запросов за 12 с бездействия, примерно 86 000 запросов в день на подключённого клиента, без каких-либо ошибок где-либо. Возврат 405 удерживает это значение ровно на 1.

Отсутствие (stateless) имеет реальную стоимость, а не только плюсы: двусторонние обмены сервер→клиент (sampling, elicitation) и незапрашиваемые уведомления *ListChanged не могут работать, потому что ответ клиента приходит как отдельный HTTP-запрос, попадающий на новый экземпляр сервера, у которого нет никакой памяти о ожидающем вызове. Уведомления о прогрессе работают — они идут через исходный поток самого запроса. Для CRUD-инструментов это неважно, но не имеся на этих возможностях не стоит строить.

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

Эндпоинт /mcp не требует аутентификации. Учетные данные Habitica живут на стороне сервера, поэтому любой, кто может получить доступ к эндпоинту, может читать и записывать весь список задач аккаунта. Это осознанное решение: auth-прокси перед MCP-эндпоинтом ломает работу клиентов MCP — и именно поэтому деплой ограничен частной сетью и одним экземпляром.

API-токен — это учётные данные на уровне пользователя Habiticа (хранится в открытом виде самим Habitica), поэтому утечка токена означает полную компрометацию аккаунта. Вся запись ляёт через редактирующую оболочку, а набор теста`и проверяет, что токен никогда не проявляется ни в одной строке.

Разработка

npm ci
npm test
npm run lint && npm run typecheck
npm run build && node dist/index.js

Лицензия

MIT

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A standalone MCP server for managing habits and quit trackers through a jhabit instance. It enables users to list trackers, log entries, and retrieve detailed statistics like streaks and abstinence time.
    -
  • A
    license
    C
    quality
    D
    maintenance
    Habitica MCP server built with Effect v4, currently exposing a hello-world tool, resource, and prompt over stdio for early development and testing.
    1
    24 npm
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP server for managing Habitica as a daily execution layer, enabling agents to read and (with explicit confirmation) create, complete, and score tasks via the Habitica API.
    24 npm
    MIT