life-hub-mcp
by mkadiya20
README.md
# Life Hub MCP
A personal remote [MCP](https://modelcontextprotocol.io) server (Cloudflare Worker) that lets
Claude log and read structured life-tracking data — starting with nutrition — into an Obsidian
vault.
You type a freeform meal in Claude ("3 eggs, oatmeal, a chicken breast, rice"); the model parses
it and estimates macros, then calls a tool; the Worker writes one immutable event file to
[Cloudflare R2](https://developers.cloudflare.com/r2/); Obsidian (via the
[Remotely Save](https://github.com/remotely-save/remotely-save) plugin) pulls those files into a
vault folder, where a [Dataview](https://blacksmithgu.github.io/obsidian-dataview/) dashboard
summarizes them.
Nutrition is the first tracker; the design generalizes to future entry types (journal, workout,
etc.) via a shared envelope + a typed body.
## How it works
```
Claude app ──parse+estimate──▶ MCP tool ──▶ Worker ──write──▶ R2 (one file per meal)
│
Remotely Save ◀──sync┘
│
▼
Obsidian vault + Dataview dashboard
```
### Storage model
- **One immutable event file per submission.** Never a read-modify-write of a shared daily note.
- Daily/weekly totals are **derived** at read time (and in the dashboard), never stored.
- R2 key mirrors the vault subpath, keyed by logical date so "last 7 days" is a cheap prefix-list:
```
Trackers/Nutrition/entries/2026-06-23/2026-06-23-190712.md
```
- Filename stem `{logical_date}-{HHMMSS}` is the idempotency key. A ULID lives inside the file as a
stable internal `id`.
### Entry format
Each entry is markdown: a YAML frontmatter envelope (common to all types) + a typed body + a
human-readable line.
```yaml
---
id: 01J8XQ2K7E9...
type: nutrition-entry
date: 2026-06-23 # logical date (6am-Toronto rule)
logged_at: 2026-06-23T23:07:12Z
source: claude
meal: dinner
calories: 820 # derived from items
protein_g: 63
confidence: medium
input_text: "3 eggs, oatmeal, a chicken breast, rice" # raw user text, kept for audit
items:
- { name: eggs, qty: 3, calories: 210, protein_g: 18 }
# ...
---
Dinner — 3 eggs, oatmeal, a chicken breast, rice
```
### Logical-date rule
Timezone `America/Toronto`; a "day" runs 6:00am → 6:00am, so a meal logged at 12:30am belongs to
the previous calendar date. Conversion goes through the IANA timezone (DST-aware), never a fixed
offset. An explicit date passed by the user is used verbatim.
## Tools
| Tool | Kind | Purpose |
|------|------|---------|
| `log_meal` | write | Log a meal as one nutrition entry; totals derived from per-unit macros × qty. |
| `log_weight` | write | Log a bodyweight measurement (weight_lbs, optional waist/body-fat/notes). |
| `edit_meal` | write | Correct a nutrition entry by its `key`; recomputes totals. `date` moves the entry. |
| `edit_weight` | write | Correct a bodyweight entry by its `key`. `date` moves the entry. |
| `get_entries` | read | Fetch entries in a date range as compact JSON. Omit `type` for all types; pass it to narrow. |
| `delete_entry` | read | Delete an entry by its `key` + `type` (delete carries no fields, so it stays generic). |
| `get_daily_totals` | read | Per-day nutrition macro rollups for trend questions. |
Writes are typed (one `log_X`/`edit_X` per entry type, so each tool's schema shows exactly that
type's fields); reads are generic over the shared envelope. New entry types are added as an
adapter in `src/registry.ts` — see the [`Adapter`](src/registry.ts) interface (type, folder,
tags, zod input shapes, `toBody`/`applyEdit`/`bodyLine`).
## Stack
- Cloudflare Workers + TypeScript
- Cloudflare R2 (`env.BUCKET`) for storage
- MCP over streamable HTTP, built on the [`agents`](https://www.npmjs.com/package/agents) SDK
- GitHub OAuth via [`@cloudflare/workers-oauth-provider`](https://www.npmjs.com/package/@cloudflare/workers-oauth-provider),
gated to an allowlist of GitHub logins
- Obsidian + Dataview for the dashboard (frontmatter properties, not inline fields)
## Development
```bash
npm install # installs deps and the lefthook pre-commit hook
npm run dev # wrangler dev (local R2 via Miniflare)
npm run check # lint + format check + type-check
npm run lint:fix # apply lint fixes
npm run format # apply formatting
```
Tools can be exercised locally against the `/mcp` endpoint with the
[MCP Inspector](https://github.com/modelcontextprotocol/inspector) or any MCP client.
## Deployment
Bindings (R2 bucket `life-hub`, KV namespace `OAUTH_KV`) are declared in `wrangler.jsonc`.
Secrets (set via `npx wrangler secret put <NAME>`):
- `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` — from a GitHub OAuth App whose callback is
`https://<worker-url>/callback`
- `COOKIE_ENCRYPTION_KEY` — e.g. `openssl rand -hex 32`
```bash
npx wrangler deploy
```
Then add `https://<worker-url>/mcp` as a custom connector in Claude and authenticate via GitHub.
Only GitHub logins in `ALLOWED_USERNAMES` (in `src/index.ts`) can use the tools.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing