Inspiration
My wife asks if we have ...breadcrumbs, After checking all four obvious spots I said no, later after she returns with freshly purchased groceries she finds the breadcrumbs in another spot that I didn't think to check. This was not a "we need an app" problem so much as "nobody knew what we had in our pantry and kitchen", and it is exactly the kind of question an AI assistant should be able to answer instantly, if it could ever actually see the pantry. I started building PrepWise Lite earlier this month on my free time as a local-only, privacy first pantry tracker with zero backend by design. when the WebMCP challenge landed, it was the first time there was a legitimate way to let an agent read that data without breaking the one promise the app makes: nothing leaves your device unless you say so,
What it does
PrepWise Lite tracks a pantry, a cookbook, and food allergies, entirely in the browser's localStorage, no accounts, no backend, no network calls once loaded. Beyond the usual inventory/recipe/shopping-list/expiration features, it now exposes 13 WebMCP tools so an agent open in the same tab can: summarize the pantry and shopping list, find what's expiring soonest, check what's missing (and by how much) from a recipe to help ground a substitution suggestion, rank recipes for weekly planning by what rescues an expiring ingredient, batch several recipes' timing so an agent can sequence them across limited stovetop/oven/air-fryer capacity, check a recipe against a recorded allergy, propose new inventory from a fridge photo, and write up a whole recipe with per-step timers, propose new inventory from a fridge photo, and write up a whole recipe with per-step timers. Every write is staged as a preview the user must confirm, never applied automatically and that preview is a refining pass rather than a yes/no gate: a quantity the model misread off a photo gets corrected in place, and any row dropped, before anything lands.
How PrepWise was built
Vanilla HTML/CSS/JS, no framework, no bundler, no runtime dependency. The CSP is connect-src 'none', so the app is structurally incapable of making a network call. "assets/js/webmcp.js" registers each tool via "document.modelContext.registerTool();" handlers run in the page next to the same state object the UI already renders from, so exposing a tool is a local function call, not a new attack surface. The governing rule: reads are answered directly, writes are staged. Every write tool builds a backup-shaped payload and routes it through the exact "validateBackup()" to "sanitizeBackup()" then to "import-preview" gate a hand-edited backup file would go through. An agent gets no shortcut a hostile file wouldn't get. The whole feature is off by default; registering nothing until the user intentionally ticks one Settings checkbox.
Challenges I ran into
- Real agents find real bugs static tests miss. An early run against a live agent returned every recipe with a blank method the step text lived in content, and the code read .text. The existing test only asserted "Ingredients:" appeared, so it passed on a recipe with an empty method. Fixed, and the test now checks for actual step text.
- Keeping "an assistant can propose, never apply" airtight under UI tampering. It's not enough to hide the "replace everything" radio button for agent-sourced imports the confirm handler independently re-checks the source of the import regardless of what the (hide-able, script-editable) radio says.
- Resisting the urge to fake domain expertise. A few tool ideas we considered such as calculating food-safety timelines during a power outage, flagging "nutritional gaps," reverse-engineering a spice blend, would have required PrepWise to invent plausible-sounding heuristics from a bare item name, with zero temperature, storage-location, or nutrition data behind them. For a food-safety context, a confidently wrong heuristic is a real harm, not just a bug. We scoped those down to "PrepWise hands over the real data it has; the agent's own food/nutrition knowledge does the actual reasoning" instead.
- Testing an API that doesn't exist yet in a test browser. WebMCP is a moving W3C Community Group draft behind a Chrome flag; Playwright's bundled Chromium doesn't support it at all. Lots of trial and error.
- Making the preview editable without turning the sanitizer into decoration. The whole point of staging a write is that nothing reaches the pantry unchecked, so adding form fields to that screen is exactly where a safety property quietly dies. The rule we settled on: an edit is still input. Edits land on the staged payload, and the confirm handler re-runs the same sanitizer over all of it before applying — nothing is trusted for having been in the page. The item's name deliberately stayed read-only, because changing it changes whether the merge sees a duplicate, so the row would have to re-decide between "will add" and "already in your pantry" as you type.
Accomplishments that I am proud of
- Added a full agent-facing tool surface without loosening the CSP by one character, connect-src 'none' on day one is still connect-src 'none' today.
- Every single write path, file import, sample data, or an agent's proposal goes through the identical "validate/sanitize/preview/confirm" gate. There is no separate, quieter door for a model to walk through.
- Allergy severity is never disclosed to an agent, matching exactly what Settings promises a human user and it's pinned by a test that stages a real conflict and asserts the severity string never appears in the output.
- 13 tools, off by default, verified against a real agent in real Chrome.
- The human-in-the-loop gate is a refining pass, not a rubber stamp. A vision model miscounts what it cannot see like six oranges in the photo is eight in the bag once the buried ones are counted. So an agent's proposed rows carry an editable quantity and unit and an Add tick: correct what it misread, drop what you don't want, then confirm, which is more efficient than throwing all the work away and doing a new call.
What I learned
- The right mental model for handing an agent access to a personal-data app isn't "sandbox what it can see" it's "answer freely, stage everything that changes something" which turned out to generalize cleanly across a dozen tools built weeks apart.
- A local-first architecture, which we'd built for privacy reasons that had nothing to do with AI, turned out to be exactly what makes an app trustworthy to hand to an agent: there's no server in the loop to leak to in the first place.
- The most valuable thing a tool can do for a model is supply ground truth the model has no other way to get such as what's actually on your shelf, what's actually about to expire, and then get out of the way not try to out-reason the model on things it already knows better such as food chemistry, nutrition, spice profiles.
- "Approve or reject" is the wrong shape for a human-in-the-loop gate when the model's mistake is small and numeric. Forcing a binary choice on a proposal that is 90% right means the user either accepts a number they know is wrong or discards work that was mostly correct, and the first of those is what actually happens. Letting them fix it in place is better UX and a stronger safety argument: the human stops being a rubber stamp and becomes the one who ultimately decides and approves the unit and value.
What's next for PrepWise Lite
More refining, and more agentic testing to try and catch anything a real agentic conversation surfaces that a test suite wouldn't. Beyond that: the two other ideas from the original design such as starting a timer, and switching views are still unbuilt, and a couple of the scoped-down ideas from this new round of edits like an honest "storage-location-aware version of the outage/storm tools" would need a deliberate data model addition rather than another heuristic, worth designing and iterating on it properly rather than rushing in under deadline pressure.
Built With
- accessibility
- amazon-web-services
- chatgpt
- chrome
- claude
- content-security-policy
- css3
- gemini
- html5
- javascript
- local-first
- localstorage
- model-context-protocol
- playwright
- privacy
- vanilla-javascript
- wcag
- web-audio-api
- webmcp
Log in or sign up for Devpost to join the conversation.