The storehouse your AI fills. Morsel is a MCP-first, camera-first food tracker. There is no chat inside the app — you log by chatting with your existing assistant (Claude, ChatGPT) and uploading a photo; the assistant's agent reads the photo, calls Morsel's MCP tools, and writes structured food data to your store. Morsel is the data store + dashboard + agent skill; the intelligence lives in the agent you already use.
People chat with Claude/ChatGPT every day but can't keep context or structured data. Generic calorie apps re-implement their own AI, locked inside a silo.
Morsel flips it:
- No in-app chat. The app has no AI brain and no chat UI.
- MCP-first. The app is a Model Context Protocol server + a data store. Your agent connects over MCP and knows the exact data structure to write.
- Camera-first. You upload a food photo in your chat app; the agent's vision
estimates macros and calls
log_meal. - Dashboard. A native iOS app (or PWA) renders your history, totals, and goals — reading the same store the agent writes.
Supabase (Postgres + auth + RLS + storage) ↔ thin remote MCP server ↔ your agent (Claude/ChatGPT) and ↔ native iOS dashboard. One store, two clients.
morsel/
├── docs/ # design docs (start here)
├── server/ # remote MCP server (Bun + Hono + MCP SDK)
├── app/ # native iOS dashboard (SwiftUI) — reads Supabase
├── db/ # Postgres migrations + seed
├── packages/schema/ # canonical types + JSON schemas for the tool contract
├── skills/ # agent skill(s) you attach to Claude / ChatGPT
└── supabase/ # project config
- ARCHITECTURE — components, data flow, auth, backend decision
- DATA_MODEL — tables, enums, RLS
- NATIVE_JOURNAL_PROVENANCE — archived native rationale, issue why-index, month-span read contract
- MCP_TOOLS — the tool contract (input/output schemas) — what the agent writes
- CUSTOM_DOMAIN — earlier planned Supabase custom-domain setup (superseded for the MCP endpoint by FLY_DEPLOY)
- FLY_DEPLOY — Fly.io single-process MCP hosting (issue #72): entry point, route/metadata contract, deploy runbook (deployed — canonical MCP endpoint)
- MIGRATION_RECOVERY — production schema reconciliation runbook (issue #76): read-only plan, human-gated apply workflow, confirmation phrase, read-back, acceptance
- TARGETS — computed calorie/macro goal from body metrics
- IN_CHAT_RENDER — Tier-1 snapshot rendering inside Claude/GPT
- ROADMAP — milestones
- CLAUDE.md — context for any agent working in this repo (
AGENTS.mdis a symlink to it)
Design scaffold with quality guardrails in place (strict TypeScript, anti-slop
ESLint + SwiftLint, CI on every PR). Working name morsel (rename freely —
it's a folder + a README).
Feature PRs target staging — the protected integration branch. Its required
status checks are quality, swiftlint, bun-fly-entrypoint and
fastfile-contract (see .github/workflows/ci.yml). main is release-only
and is promoted from staging by a human. Production/CD remains
human-dispatched: deploys run only via explicit workflow_dispatch, and
migration apply runs only through an explicit human-enabled dispatch/flag,
while the main push trigger performs read-only classification only — the
classify job in .github/workflows/migration-cd.yml.
Migrations NEVER auto-apply: the apply workflow is workflow_dispatch-only
and targets the production environment (issue #76). A merge to main
causes zero production SQL. Production was provisioned out of band and has no
ledger, so schema repair runs through the verified recovery runner under an
explicit human dispatch — see
docs/MIGRATION_RECOVERY.md for the five phases
(read-only plan → reviewed code → human-confirmed dispatch → read-back →
live-app acceptance) and the issue #76 confirmation phrase. Blind --adopt
was removed: historical migrations may only be recorded after the recovery
runner verifies each migration's complete end-state contract. Once the ledger
exists, future 000N migrations are appended by
node scripts/apply-migrations.mjs, which executes each migration and its
ledger insert as ONE atomic BEGIN..COMMIT request and never bootstraps the
ledger (missing/empty ledger → zero writes, fail closed).
TestFlight is dispatch-only and requires the testflight environment's ASC API
key and iOS distribution certificate secrets.
The MCP server runs on Fly.io single-process hosting (issue #72): the
canonical client-facing endpoint is https://mcp.morselfood.app/mcp
(custom domain over the Fly deployment, issue #130; runbook in
docs/FLY_DEPLOY.md), so the in-memory MCP session map
survives across requests. During the transition the legacy
https://morsel-mcp.fly.dev/mcp origin still serves the identical transport
and metadata — both are valid MORSEL_PUBLIC_BASE_URL values and no client
is forced to migrate. The Supabase Edge
Function deployment is retained as legacy compatibility. Vercel remains the
browser consent page and posts to the Fly origin's /mcp/authorize (issue
#74).
MIT — see LICENSE.