DEV Community

Alyssa Pratt
Alyssa Pratt

Posted on

Pantry & Plate: An AI Kitchen Assistant That Actually Knows What's Expiring Tomorrow

Notion MCP Challenge Submission 🧠

This is a submission for the Notion MCP Challenge

What I Built

I buy in bulk at Costco to save money. The problem: tracking 100+ pantry items, remembering what's expiring, and figuring out what to cook for a two-person household with different food preferences.

Pantry & Plate is an AI-powered pantry tracker and meal planner that eliminates that cognitive load. It parses grocery receipts using Claude's vision, tracks expiry dates in Notion, and runs an agentic chat assistant that knows exactly what's in my kitchen, what needs to be used first, and what my partner won't eat (mushrooms, shrimp, brussels sprouts).

Here's what makes it different: when I ask "What can I make tonight?", the agent doesn't guess. It queries my actual Notion databases β€” pantry items with real expiry dates, my household preferences, my planned meals for the week β€” then reasons over the results before suggesting anything.

The agent might respond: "The Italian sausage expires in four days. How about a quick sausage pasta with the San Marzano tomatoes from your pantry? I'll add it to your meal agenda."

That's not a canned response. The agent read Expiry Date: March 30 from my Notion Pantry database.

Key Features

  • Receipt parsing with human-in-the-loop review: Upload a photo of a Costco receipt. Claude's vision parses "KS ORG STOCK" into "Kirkland Organic Chicken Stock" (it guesses). You review and correct before saving.
  • Compact pantry view: 100+ items grouped by category, collapsible sections, inline editing, expiry date tracking.
  • 11-tool conversational agent: Query pantry, shopping list, meal history, preferences, kitchen tools, and meal agenda β€” all via natural language.
  • Shopping list with purchase-to-pantry flow: Check "purchased" β†’ item moves to pantry automatically.
  • Preferences as silent context: The agent knows my partner hates mushrooms. It never narrates this β€” just excludes them.

Video Demo

Show us the code

https://github.com/Aprattcodes/pantry-tracker

Stack

  • Next.js 15, TypeScript, Tailwind v4, shadcn/ui
  • Anthropic Claude API (vision for OCR, forced tool use for parsing, agentic loops)
  • Notion as the persistent data layer (6 databases)
  • @notionhq/client v2

How I Used Notion MCP

We implemented the MCP tool-use pattern β€” Claude decides which of 11 tools to call, queries live Notion data, and reasons over results in an agentic loop β€” using @notionhq/client v2 with integration tokens.

We initially attempted mcp.notion.com but it requires OAuth authentication, which doesn't fit a server-side web application architecture where the backend needs persistent database access. The agentic pattern is structurally identical to an MCP client-server interaction: tool discovery, tool invocation, result handling, multi-turn reasoning. The transport layer differs, but the intelligence pattern is the same.

The Agentic Loop in Action

Here's how chatAgent() works:

// 11 tools available to the agent
const CHAT_TOOLS = [
  { name: 'query_pantry', description: 'Get all pantry items with expiry dates' },
  { name: 'query_preferences', description: 'Get household food preferences' },
  { name: 'query_meal_agenda', description: 'Get planned meals for the week' },
  { name: 'suggest_meals', description: 'Generate meal suggestions' },
  { name: 'add_to_meal_agenda', description: 'Add a meal to the weekly plan' },
  { name: 'log_meal', description: 'Log a meal to history' },
  // ... 5 more tools
];

// The loop: Claude calls tools, we execute, Claude reasons over results
while (response.stop_reason === 'tool_use' && iterations < 8) {
  const toolUse = response.content.find(c => c.type === 'tool_use');

  // Execute against real Notion databases
  const result = await executeNotionTool(toolUse.name, toolUse.input);

  // Feed result back to Claude for reasoning
  messages.push({ role: 'user', content: [{ type: 'tool_result', content: result }] });
  response = await anthropic.messages.create({ model, messages, tools: CHAT_TOOLS });
}
Enter fullscreen mode Exit fullscreen mode

When the user says "What can I make tonight?", Claude decides to:

  1. Call query_preferences β†’ learns one of us hates mushrooms, we both love Mexican food
  2. Call query_pantry β†’ sees Italian sausage expires in four days
  3. Call query_meal_agenda β†’ checks what's already planned
  4. Call suggest_meals β†’ generates contextual suggestions

The agent makes multiple Notion queries in a single conversational turn, reasoning over real data β€” not summaries.

Notion as the Data Layer

Six databases power the entire system:

Database Purpose
Pantry Items with category, quantity, expiry date, store
Shopping List Items grouped by store with purchase status
Meals Log Meal history with date, category, effort level
Preferences Likes, hard-passes, cuisine preferences per person
Kitchen Tools Equipment with effort levels (sous vide = High)
Meal Agenda Currently planned meals for the week

The agent can query any of these, cross-reference them, and write back. When I say "Add Italian sausage pasta to my meal plan," it:

  1. Calls add_to_meal_agenda β†’ writes to Meal Agenda DB
  2. Auto-triggers log_meal β†’ writes to Meals Log DB

Two Notion writes from a single conversational command.

Human-in-the-Loop Receipt Parsing

Costco receipts are cryptic. "KS ORG STOCK" means Kirkland Signature Organic Chicken Stock. We use Claude's vision with forced tool use to guarantee structured output:

const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-20250514',
  max_tokens: 4096, // Required for 27+ item receipts
  tool_choice: { type: 'tool', name: 'record_pantry_items' },
  // ...
});
Enter fullscreen mode Exit fullscreen mode

The parsed items appear in a review table β€” original receipt text on the left, Claude's interpretation on the right. The human corrects mistakes, picks expiry dates, then saves. The review step is critical: Claude makes educated guesses, but a human confirms before anything hits Notion.

Key Technical Decisions

Forced tool use for structured output: tool_choice: { type: "tool", name: "record_pantry_items" } guarantees JSON output. Prompt engineering alone was unreliable for 27+ item receipts.

max_tokens: 4096 for long receipts: Below this threshold, Claude silently truncates. Discovered this the hard way with a Costco run.

Preferences as silent context: Early versions narrated preferences ("I see you don't like mushrooms..."). Now the agent applies them without explanation β€” like a real kitchen assistant would.

Store inference from preferences: When the user says "add ground beef to my shopping list," the agent queries preferences, finds "household buys ground beef at Costco," and auto-sets store=Costco, quantity=3 lbs.

Purchase-to-pantry flow: Checking "purchased" on a shopping list item creates a pantry entry with the same name/store/quantity. One checkbox replaces a manual two-step process.


Reflections

Building this taught me that the MCP pattern isn't about the transport layer β€” it's about giving an AI agent live data access and the autonomy to query what it needs. Whether that's via a remote MCP server or a custom tool wrapper around @notionhq/client, the intelligence pattern is the same: tool discovery, invocation, result handling, multi-turn reasoning.

The hardest part wasn't the agentic loop β€” it was the UX around it. Making tool calls visible (we show "tool pills" in the chat), handling the max_tokens stop reason gracefully, and ensuring the agent doesn't narrate its reasoning when it should just act.

Notion as a data layer worked surprisingly well. The real-time queries mean the agent always has accurate state. No stale caches, no sync issues. When I buy groceries and check them off, the next "What can I make?" query sees the updated pantry.

For a two-person household that meal plans around Costco runs and expiring produce, this is the tool I actually wanted to exist.


Built by a developer who got tired of throwing away forgotten produce.

Top comments (0)