habitica-mcp

habitica-mcp

Enables managing tasks, tags, and user stats on a self-hosted Habitica instance through MCP tools over Streamable HTTP.

Category
访问服务器

README

habitica-mcp

An MCP server for a self-hosted Habitica instance, served over Streamable HTTP so it can run as a normal networked service rather than a per-client stdio subprocess.

Why this exists

The existing community server (iBreaker/habitica-mcp-server) hardcodes https://habitica.com/api/v3, is stdio-only, and has been unmaintained since three days after it was created. None of that works for a self-hosted instance behind an ingress.

Here, HABITICA_BASE_URL is required with no default — pointing at the wrong instance is made impossible rather than merely discouraged.

Tools

Tool Notes
list_tasks Optional type filter; history excluded (see below)
get_task
create_task Not idempotent — Habitica has no idempotency key
update_task Partial update
delete_task Destructive
score_task Destructive — mutates gold/XP/streaks, cannot be undone
list_tags / create_tag
add_tag_to_task / remove_tag_from_task Take a tag name, resolved to its UUID
get_user_stats Server-side projection, not the full user document

Configuration

Variable Required Default Purpose
HABITICA_BASE_URL yes — e.g. http://habitica.tools.svc.cluster.local:3000
HABITICA_USER_ID yes — x-api-user
HABITICA_API_TOKEN yes — x-api-key
MCP_ALLOWED_HOSTS no (empty — validation off) Comma-separated Host allowlist for /mcp
MCP_HOST / MCP_PORT no 0.0.0.0 / 8080
HABITICA_TIMEOUT_MS no 15000
LOG_LEVEL no info

Endpoints: POST/GET/DELETE /mcp, and GET /healthz.

Design notes

Four decisions that are load-bearing and non-obvious:

Response projection, not pagination. Habitica's GET /tasks/user returns history: [{date, value}] on every habit and daily — one entry per scoring event for the life of the account, and it is on by default. The API offers no limit/offset, so the fix is projection: this server always sends history=false and additionally projects each task to a fixed field set, so an upstream schema change cannot silently reintroduce hundreds of KB into a model's context. get_user_stats uses ?userFields= for the same reason.

The list filter is plural and irregular. GET /tasks/user?type= accepts habits | dailys | todos | rewards | completedTodos (note dailys), while the create body takes the singular habit | daily | todo | reward. Tools expose the singular form and map internally; passing the singular form to the list endpoint 400s.

Host validation is scoped to /mcp, never app-wide. createMcpExpressApp applies it globally, which would break both kubelet probes (an httpGet probe sends Host: <podIP>, and pod IPs can never be allowlisted) and blackbox monitoring (which sends Host: <svc>.<ns>.svc). /healthz therefore sits outside the guard; it exposes nothing, and DNS-rebinding protection only matters for the JSON-RPC surface.

/healthz reports process liveness only — never Habitica reachability. A connectivity check would turn a Habitica restart into a CrashLoopBackOff here, and the liveness probe would then keep killing a process that is perfectly healthy and simply has nothing to talk to. Habitica outages surface as clean per-tool JSON-RPC errors instead.

Transport

Stateless Streamable HTTP (sessionIdGenerator: undefined), built on @modelcontextprotocol/server v2 — the current stable major, whose HTTP transport lives in the separate @modelcontextprotocol/express / @modelcontextprotocol/node adapters. The negotiated protocol version is 2025-11-25 (LATEST_PROTOCOL_VERSION in the SDK); v1.x is now security-and-bugfix only.

A fresh McpServer + transport is created per request, torn down on the response's close event. Per-request construction is required rather than tidy: SDK v1 throws outright on stateless transport reuse ("Stateless transport cannot be reused across requests"), because reuse causes message-ID collisions between concurrent clients.

The cost is real and worth knowing: each request rebuilds 11 zod→JSON-Schema conversions, which measured at roughly 0.5 MB of garbage per call. It is reclaimed under GC pressure (1500 sequential calls settled at ~193 MiB with a 96 MB heap cap) rather than leaking, but it is why the deployment requests more memory than the idle footprint suggests.

GET /mcp returns 405 with Allow: POST. This is spec-legal (a server may refuse the standalone stream) and is what the MCP client explicitly expects — it special-cases 405 as "no server stream here" and stops.

An earlier version tried to be accommodating by returning an empty SSE stream instead. That caused an infinite reconnect loop: the client treats a cleanly-ended stream that carried no response as a dropped connection and reschedules, but its retry counter only advances on failure, so a successful empty stream reset nothing. Measured at ~1 req/s forever — 1 → 4 → 8 → 12 GETs over 12s idle, roughly 86k requests/day per connected client, with no error surfaced anywhere. Returning 405 holds it at exactly 1.

Stateless has a real cost, not just upside: server→client round-trips (sampling, elicitation) and unsolicited *ListChanged notifications cannot work, because the client's reply arrives as a new HTTP request that lands on a fresh server instance with no memory of the pending call. Progress notifications do work — they ride the originating request's own stream. None of that matters for a CRUD tool surface, but do not build on those capabilities here.

Security

The /mcp endpoint is unauthenticated. The Habitica credential lives server-side, so anyone who can reach the endpoint can read and write the account's entire task list. This is deliberate — an auth proxy in front of an MCP endpoint breaks MCP clients — and it is why the deployment is restricted to a private network and a single replica.

The API token is a user-level Habitica credential (stored in plaintext by Habitica itself), so leaking it is full account compromise. All log output passes through a redacting logger, with a test asserting the token never appears in any emitted line.

Development

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

License

MIT

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选