# HabitLog Pro — AI connector (MCP)

Check in habits and read streaks from Claude or ChatGPT. The connector is a remote MCP
server (Streamable HTTP) at:

    https://habitlogpro.com/mcp

## Connect

**Claude:** Settings → Connectors → Add custom connector → paste the URL → Connect. Sign in
with Google on the HabitLog Pro screen (this creates a free account if you don't have one)
and click **Allow**.

**ChatGPT:** Settings → Apps & Connectors → Advanced → Developer mode, then Create → paste
the URL, authentication OAuth.

Authentication is OAuth 2.1: dynamic client registration (RFC 7591), PKCE S256,
protected-resource metadata (RFC 9728) at `/.well-known/oauth-protected-resource/mcp`,
authorization-server metadata (RFC 8414). Access tokens last 1 hour; refresh tokens 90 days,
rotated on every use. Only token hashes are stored. Scopes: `read`, `write`. Disconnect any
time in the app under Settings → AI assistant.

## Designed for "yesterday I did X, Y, skipped Z"

One message becomes ONE `log_habits` call, done and skipped alike. Habit names are matched
loosely (name, alias, slug, then a unique close spelling: "meditate" → Meditation). If any
name doesn't match, nothing is logged and the error lists every problem plus the user's
habits, so the assistant asks once. Nicknames become aliases with `update_habit`, so the
next message needs no questions. Vague amounts ("had a few drinks") are logged as
placeholders (`needs_detail` + note): the dashboard shows a "check-ins to refine" card, and
any new check-in for that habit and day, from the app or the assistant, clears it.

## Tools

Dates are `YYYY-MM-DD`, default today in the user's timezone (the dashboard saves the
browser's timezone in settings; America/Toronto until it has).

| Tool | Arguments | Returns |
|---|---|---|
| `log_habits` | `entries: [{habit, done?, count?, drinks?, caffeine_mg?, note?, needs_detail?}]` (max 50), `date?` | per habit: status, matched name (`matched_from`), detail; day totals |
| `clear_checkins` | `entries: [{habit, date}]` | `cleared`, `not_found` |
| `list_habits` | `include_archived?` | habits: id, name, icon, kind, schedule, aliases, today's status, current streak or % on track |
| `get_day` | `date?` | every active habit: status (good/missed/partial/not_logged) and detail |
| `get_streaks` | `habits?` (default all active) | daily: current + best streak, good days in 30; weekly / every-N-days: % on track, last done, next due |
| `get_range` | `start`, `end` (≤366 days) | per habit good/missed/not logged; per day the good habits |
| `create_habit` | `name`, `icon?`, `schedule?` (daily / n_per_week + `times_per_week` / every_n_days + `interval_days`), `times_per_day?` (1–4), `aliases?` | the habit |
| `update_habit` | `habit` + any of `name`, `icon`, schedule fields, `times_per_day`, `aliases` | the habit |
| `archive_habit` | `habit`, `archived?` (false restores) | `{archived}` |
| `lookup` | `query` (a term or a list) | per term: `match` or `problem` + `candidates` |
| `get_entries_to_refine` | `days?` (30, max 365) | check-ins still flagged |
| `submit_feedback` | `text`, `category` | `{received, id}`; stored and sent to the developer |

Kinds (from `list_habits`):
- `check`: yes/no. `done` (default true).
- `count`: several times a day (`times_per_day` 2–4). `count` = times done; done when it
  reaches the target. Stored as `{count}` like the habit page.
- `alcohol`: a habit to avoid. `drinks` (0 = alcohol-free); stored as standard drinks
  (355 ml at 5%) like the drink picker. `done: true` with no count logs one drink, flagged.
- `caffeine`: `caffeine_mg` (0 = none). `done: true` with no amount is flagged.

Rules:
- One check-in per habit per day: logging again replaces it (the app works the same way).
  The same habit twice in one call is an error.
- `log_habits` is all-or-nothing; an identical call within 60 s returns the first result.
- Schedules the app doesn't have (e.g. specific weekdays) are refused rather than
  approximated.

Annotations: every tool has a title, `openWorldHint: false`, and read-only / destructive /
idempotent hints. Read-only: `list_habits`, `get_*`, `lookup`. Destructive: `clear_checkins`,
`update_habit`, `archive_habit`.

## Security
- Every tool is scoped to the signed-in user; other users' habits and ids are invisible.
- `/mcp` rejects browser requests from origins other than claude.ai, claude.com,
  chatgpt.com and chat.openai.com; server-to-server calls send no Origin.
- 60 requests per minute per user × app. nginx keeps no access log for `/mcp`.
- Each tool call is recorded in `mcp_calls` (tool, ok, duration, app; no arguments).

## Account and policies

Privacy: /privacy (FR /fr/confidentialite) · Terms: /terms · Support: /support · Docs page: /docs/mcp
(this file is served raw at /docs/mcp.md). Users export everything (`GET /api/account/export`) or
delete their account (`POST /api/account/delete`, `{"confirm": "DELETE"}`) from Settings → Account.
Directory reviewer accounts sign in with a password at `/auth/password` (set with
`python account.py set-password EMAIL`); anyone can get an emailed sign-in link at `/auth/email`.
Client ID metadata documents (CIMD) are supported alongside DCR; public clients revoke without a secret.
