A personal meal-planning agent you talk to like a friend who knows your kitchen. It plans the week's dinners from a shared recipe corpus, keeps track of what's in your pantry, and fills your Kroger cart β or walks you through any store, aisle by aisle. The agent runs inside Claude.ai; this repo is what builds it.
There is no data in this repo. It holds the agent's source β a Cloudflare Worker (the
yampserver), the persona it runs on, and the build tooling. Every operator's recipes live in Cloudflare R2 and their profile/state in Cloudflare D1; a separate public data repo is the deploy control plane and the plugin marketplace. See Self-hosting.
- Plans your week. Proposes a menu from your recipe corpus, weighted by your taste, the season, the weather, what you've cooked recently, what's on sale, and what's about to spoil in your fridge β then builds the grocery list.
- Tracks your kitchen. Pantry, staples, and equipment, with a nudge about perishables before they turn and storage tips when fresh produce comes in.
- Fills your Kroger cart. Resolves each ingredient to a specific SKU and writes your cart, asking only when a choice is genuinely ambiguous ("which olive oil?") β and remembering your answer so it asks less over time.
- Or walks you through any store. The same buy list, grouped aisle-by-aisle for an in-store trip and read out hands-free, one aisle at a time β Kroger or otherwise.
- Finds new recipes. Surfaces candidates from your trusted recipe blogs (RSS) and forwarded newsletters, scored against your taste; you import the keepers in one step.
- Shares with a small friend group. Recipes and store maps are shared across the group; your favorites, notes, pantry, preferences, and cart stay private to you.
you βΊ make me a menu for the week β feeling a bit lazy, and I've got salmon to use up
βΊ reads your profile, pantry, what's on sale, and the last month of dinnersβ¦
Here's an easy week, with the salmon up front:
Mon Sheet-pan salmon & green beans (uses the salmon)
Tue Leftovers
Wed Black bean tacos (pantry-heavy, 20 min)
Thu Chicken katsu curry β two others favorited this
Fri Margherita pizza (mozzarella's on sale this week)
New this week from your feeds: a miso-butter pasta β want it in?
you βΊ swap Friday for the pasta, then order it
βΊ Done. Cart's resolved β one thing to pick: 3 brands of white miso match.
β¦ you choose β¦ cart written. Check out in the Kroger app.
State lives in your data, not the chat β each conversation starts fresh and the agent reads what it needs through its tools.
The whole design turns on one split: the LLM does the fuzzy work; everything deterministic is plain code. Claude handles conversation, menu reasoning, and the genuinely-fuzzy judgment calls; a stateless Cloudflare Worker owns everything that should be exact β file I/O, recipe filtering, Kroger SKU matching, cart writes, validation.
You, in Claude.ai (web + mobile) β or the member web app at <worker>/
β the yamp plugin β workflow skills + the yamp connector
β MCP over HTTPS, OAuth 2.1 Β· connect once with an operator-issued invite code
β (the web app signs in with the SAME invite code β a cookie session + typed /api)
βΌ
Cloudflare Worker Β· yamp
β OAuth provider + multi-tenant gate + coarse, opinionated domain tools
β (pantry Β· recipes Β· Kroger matching Β· cart) β the locus of determinism
ββββββββββββΊ Cloudflare R2 β authored recipe & guidance markdown
ββββββββββββΊ Cloudflare D1 β profile, session state, indexes, caches
ββββββββββββΊ Kroger Developer API β product search, prices, cart writes
Two patterns recur and explain most of the design:
- Coarse, opinionated tools. A tool wraps a whole pipeline β
match_ingredient_to_kroger_skuruns the full ingredientβSKU match internally β so the model can't bypass the cache, validation, or matching. Raw building blocks aren't exposed, and tools return structured errors, not throws. - Capture β retrieve β narrow. LLM-derived knowledge (recipe facets, embeddings, the sale flyer) is captured once into persistent data, retrieved deterministically, and narrowed by the model with live context β so the hot path stays fast and the model is reserved for real novelty.
Two member surfaces, one Worker. Claude.ai is the conversational surface; the member web
app β a React SPA the same Worker serves at / (packages/app + the shared packages/ui) β
signs in with the same operator-issued invite code (a revocable cookie session) and talks to a
typed /api JSON surface that calls the same operations the MCP tools call.
Multi-tenancy is a D1 column. One self-hosted Worker serves a friend group; an invite code
resolves to a tenant before any tool runs, and every per-tenant table is isolated by its tenant
column. Recipes and store maps are deliberately shared; everything personal is not.
The full picture β the determinism boundary, the data model, the Kroger matching pipeline, and the
background crons β is in docs/ARCHITECTURE.md.
| Path | What it is |
|---|---|
src/, test/, wrangler.jsonc |
the Cloudflare Worker β the yamp MCP server + OAuth provider |
packages/app, packages/admin-app, packages/ui |
the member web app (a React SPA served by the Worker at /), the operator admin panel (a React SPA served at /admin), and the shared shadcn/ui components + theme tokens both build on |
scripts/ |
build tooling β recipe indexes, the static cookbook |
packages/plugin/AGENT_INSTRUCTIONS.md |
the agent persona + conversational flows; the source the plugin bundle is generated from (the bundle is published to the operator's data-repo marketplace, not committed here) |
docs/ |
the deep docs (see Documentation) |
migrations/d1/ |
D1 schema migrations, applied by wrangler d1 migrations apply |
openspec/ |
the change/spec workflow β changes/archive/ is the history, specs/ the living contract |
.github/workflows/ |
CI plus the reusable workflows operators' data repos call |
Built with Cloudflare Workers (TypeScript on workerd) Β· D1 + KV Β· a GitHub App for repo I/O Β·
the Kroger Developer API Β· pure-JS parsers (js-yaml, JSON-LD via HTMLRewriter, RSS via
fast-xml-parser). No database server, no scheduler, no stateful runtime.
The toolchain is pinned with mise; aube is the package manager (aubr
= aube run). Don't install anything globally β aube-lock.yaml is the lockfile and
pnpm-workspace.yaml defines the packages/* workspaces.
mise install # Node 22 + aube, pinned in mise.toml
aube install # dependencies (reads aube-lock.yaml in place)
aubr dev # wrangler dev β a local Worker; point MCP Inspector at the local URL
aubr dev:app # wrangler dev + the member app's Vite dev server (HMR; /api proxied)
aubr typecheck # tsc --noEmit
aubr test # vitest β Worker unit tests (test/*.test.ts)
aubr test:tooling # node --test β build-tooling tests (tests/*.test.mjs)
aubr build:plugin # throwaway/inspect build β dist/ (the deploy publishes the real bundle to your data-repo marketplace)Local dev secrets (a GitHub App key + Kroger credentials) go in a gitignored .dev.vars β see
.dev.vars.example. The full developer guide β Worker dev, the D1 workflow,
deployment, and conventions β is CONTRIBUTING.md.
One self-hosted Worker serves a small friend group, and you don't fork this repo to run it.
You create a public data repo from a template β that repo is your control plane and your
plugin marketplace: it holds your config, a thin caller workflow that references this repo's reusable
CI, and the plugin bundle your deploy publishes with your connector URL baked in. Your recipes live
in a Cloudflare R2 bucket; Cloudflare hosts the Worker, D1, KV, and R2 (comfortably free-tier at
personal scale); a Kroger developer app handles search and cart. Friends add your marketplace
(/plugin marketplace add <you>/yet-another-meal-planner-deployment) and connect their own Claude.ai with an
invite code you mint β no GitHub or Kroger developer account needed on their end. Nothing in the
data repo is secret, which is what lets it be public.
The complete walkthrough is docs/SELF_HOSTING.md.
CONTRIBUTING.mdβ toolchain, Worker dev, deployment, conventions. Start here to hack on it.docs/ARCHITECTURE.mdβ the determinism boundary, multi-tenancy, the data model, the Kroger matching pipeline, the crons.docs/TOOLS.mdβ the MCP tool contract (params, returns, guarantees).docs/SCHEMAS.mdβ recipe-file and D1 formats.docs/SELF_HOSTING.mdβ operator setup, end to end.docs/authoring-store-adapters.mdβ writing the per-store satellite adapters (sale-scan + cart-fill) for a store the Worker has no API for.AGENT_INSTRUCTIONS.mdβ the persona and the conversational flows.
A personal automation experiment aimed at a real friction point β the time and willpower grocery planning takes β tuned to one person's tastes, freezer, and grocer, and shareable with a few friends. Not a product, not a startup. The architecture is deliberately minimal: Claude provides the reasoning, the Worker provides a domain interface, GitHub holds the recipe corpus and its history, and D1 holds the operational data. The recipe files are plain, version-controlled markdown β inspectable by a human and able to outlive the agent if anyone ever stops using it.
AGPL-3.0-only β open source, copyleft. Use it, modify it, and self-host it freely, including running your own instance for yourself and your friends at no charge. The one obligation: run a modified version as a network service and AGPL Β§13 asks you to offer those users its source.
It's also dual-licensed. The maintainer reserves the right to offer a managed, hosted version under separate commercial terms β so a commercial use the AGPL doesn't permit (a closed-source hosted offering) needs a commercial license; ask the maintainer. Self-hosting always stays free.
Contributions come in under the AGPL plus a short Contributor License Agreement β the grant that keeps the dual-license possible. See CONTRIBUTING.md.