Skip to main content
Glama
LinusU

mise-mcp

by LinusU

mise-mcp

Mise is a tiny shared source of truth for a household's food inventory, meal plan, recipes and preferences, exposed as a remote MCP server. Connect ChatGPT, Claude or any other MCP-capable assistant and talk to it:

  • "I bought these, add them to the inventory."

  • "We used the chicken thighs and half the broccoli for dinner."

  • "What's in the freezer?" / "That cream expires tomorrow, can we use it?"

  • "Plan dinners for next week." / "Ella wants noodles tonight, rearrange the plan."

  • "We cooked the planned wok, update the inventory."

The assistant does the fuzzy reasoning. Mise stores reliable structured state and offers coarse, atomic operations to read and change it. There is no LLM inside Mise.

Runs on Cloudflare Workers + D1 and fits comfortably in the free tier.

How it works

ChatGPT / Claude / Claude Code / MCP Inspector
        │  HTTPS, Streamable HTTP MCP, Authorization: Bearer <token>
        ▼
Cloudflare Worker  (src/index.ts)
  ├─ /mcp                      MCP endpoint (stateless, one server per request)
  ├─ /.well-known/*            OAuth discovery metadata
  ├─ /oauth/*                  tiny OAuth 2.1 authorization server (login = access key)
  └─ D1 (SQLite)               households, members, inventory, meals, recipes,
                               preferences, audit log, tokens
  • Inventory rows are physical lots. "500 g chicken expiring tomorrow" and "1 kg chicken in the freezer" are two rows; nothing is merged by name. Quantities are quantity + unit, or a free-text quantityText ("some").

  • Meals live on a calendar: date, meal type, title, status (planned / cooked / skipped / cancelled), optional recipe link.

  • Recipes are lightweight: name, description, ingredient list with rough amounts, free-text instructions, tags, servings.

  • Preferences are short natural-language statements ("Ella likes noodles").

  • Changes is an append-only audit log with the acting member, the client (ChatGPT, Claude Code, ...) and a readable summary.

Every mutation is one D1 batch(), which runs as a single transaction: either all of "consume 450 g chicken, use up the noodles, mark dinner cooked" happens or none of it does.

MCP tools

Tool

Purpose

get_household_context

Members, today's date in the household timezone, default servings, all preferences. Call first.

get_inventory

Current items; filter by location, search, expiringWithinDays.

get_expiring_inventory

Items expiring within N days (default 3).

add_inventory_items

Add one or more lots.

update_inventory_items

Change fields on existing lots (null clears).

consume_inventory

Record usage per item (amount, usedAll, or remaining/remainingText); optional mealId marks that meal cooked atomically.

remove_inventory_items

Delete lots (thrown away, mistakes).

get_meal_plan

Meals in a date range (default next 7 days).

plan_meals

Create or replace meals for several days at once (by id or by date + type).

update_meals

Move, rename, change status, swap (two updates in one call).

delete_meals

Hard delete.

search_recipes, get_recipe, save_recipe, update_recipe, delete_recipe

Recipe library.

set_preference, remove_preference

Durable planning preferences.

get_recent_changes

Audit log, newest first.

Responses are compact JSON with stable ids (inv_…, meal_…, rcp_…, pref_…), returned both as text and as structuredContent. Validation problems come back as tool errors with a hint, and nothing is written when any part of a batch is invalid.

Authentication

Every request to /mcp needs Authorization: Bearer <token>. Tokens are random strings; only their SHA-256 hash is stored in D1, bound to a household member. There are two ways to get one, and both end up as the same kind of token:

  1. Static API token (Claude Code, MCP Inspector, curl, scripts): npm run token:create -- Linus --label "Claude Code" --remote.

  2. OAuth 2.1 (ChatGPT, Claude.ai, Claude Desktop): the Worker is also a minimal authorization server. The assistant discovers it through the standard 401 + /.well-known/oauth-protected-resource handshake, registers itself (dynamic client registration or a client-ID metadata document), and sends the user to a sign-in page where they pick who they are (Linus / Ella) and type the household access key (the MISE_ACCESS_KEY secret). PKCE S256 is required; refresh tokens rotate.

Why both? As of September 2026, ChatGPT only connects to authenticated MCP servers via OAuth, Claude.ai supports OAuth (static headers are in a limited beta), while Claude Code and the MCP Inspector send a plain bearer header. Secrets never go in query strings and nothing secret is committed.

Because each token belongs to a member, the audit log records who made a change and through which client.

Setup

1. Prerequisites

  • A Cloudflare account (free plan is enough: Workers + D1).

  • Node.js 20+ and npm.

  • npx wrangler login once, so Wrangler can deploy.

npm install

2. Create the D1 database

npx wrangler d1 create mise

Copy the printed database_id into wrangler.jsonc (replace the zeros).

3. Apply migrations

Local (used by npm run dev and the tests):

npm run db:migrate

Remote (production):

npm run db:migrate:remote

4. Seed the household

seed/household.sql creates the household "Linus & Ella", its two members and a few starting preferences. It is idempotent and contains no fake inventory.

npm run seed:remote

For local development, npm run seed runs the household seed and seed/dev.sql, which adds a known dev token (mise-dev-token, belongs to Linus) plus sample inventory, a recipe and a planned meal. Never run dev.sql against production.

To rename the household or members, edit seed/household.sql before running it (or update the rows with wrangler d1 execute).

5. Set the access key secret

The access key is what a person types on the OAuth sign-in page.

npx wrangler secret put MISE_ACCESS_KEY

Locally, copy .dev.vars.example to .dev.vars (already ignored by git) and set the value there.

6. Local development

npm run dev

The Worker listens on http://localhost:8787. Try it:

curl -s localhost:8787/mcp -X POST \
  -H "content-type: application/json" \
  -H "accept: application/json, text/event-stream" \
  -H "authorization: Bearer mise-dev-token" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_inventory","arguments":{"location":"fridge"}}}'

Checks:

npm run check        # typecheck + lint + tests
npm test             # vitest, runs inside the Workers runtime against a local D1

7. Deploy

npm run deploy

8. Find the public MCP URL

Wrangler prints the Worker URL on deploy, e.g. https://mise-mcp.<your-subdomain>.workers.dev. The MCP endpoint is that URL plus /mcp. GET / on the Worker echoes it back.

9. Create a token (for bearer-header clients)

npm run token:create -- Linus --label "Claude Code" --remote

The token is printed once. Drop --remote to create one in the local database.

10. Connect an MCP client

Claude Code

claude mcp add --transport http mise https://mise-mcp.<subdomain>.workers.dev/mcp \
  --header "Authorization: Bearer <token>"

ChatGPT (Settings → Apps & Connectors → Create, requires developer mode): enter https://mise-mcp.<subdomain>.workers.dev/mcp, choose OAuth. ChatGPT discovers the authorization server, opens the Mise sign-in page, and you pick your name and enter the access key. Each person connects from their own ChatGPT account and gets their own token.

Claude.ai / Claude Desktop (Customize → Connectors → Add custom connector): enter the same URL and leave the OAuth fields empty; Claude detects the OAuth server and runs the same sign-in flow. If your organization has the "Request headers" beta you can instead add Authorization: Bearer <token>.

MCP Inspector

npx @modelcontextprotocol/inspector

Transport "Streamable HTTP", URL …/mcp, add an Authorization header with Bearer <token>.

11. Test that it works

Ask the assistant:

  1. "What food is in the fridge?"

  2. "Add 1.2 kg chicken thighs to the fridge, expiring in 3 days, and a package of egg noodles to the pantry."

  3. "Plan chicken noodle wok for tomorrow's dinner."

  4. "Show the meal plan for the next week."

  5. "We cooked the wok: used 450 g chicken and the whole package of noodles."

  6. "How much chicken is left?"

  7. "What changed recently?"

The same sequence runs automatically in test/mise.test.ts.

Project layout

src/index.ts        fetch router: health, OAuth, /mcp
src/mcp.ts          builds the MCP server, registers tools
src/tools/          inventory.ts, meals.ts, recipes.ts, household.ts, shared.ts
src/auth.ts         bearer token verification and issuance
src/oauth.ts        OAuth 2.1 authorization server + sign-in page
src/db.ts           ids, dates, hashing, audit-row helper
migrations/         D1 SQL migrations (wrangler d1 migrations)
seed/               household.sql (safe for prod), dev.sql (local only)
scripts/            create-token.mjs
test/               vitest suites running inside workerd

Design notes

  • No grocery ontology. An item is what a human would call it plus a few structured fields. The assistant knows "ICA chicken thighs" is chicken.

  • Lots, not aggregates. Separate rows per purchase keep expiry tracking honest. Merging is a future decision, not a schema constraint.

  • Coarse, atomic tools. Every mutation takes a list and runs as one D1 batch. This keeps conversational multi-step changes to a single call.

  • Stateless MCP. One server instance per HTTP request, no sessions, no Durable Objects. The SDK's Workers-safe JSON-schema validator is used because the default one relies on new Function.

  • Two protocol eras. Requests from 2025-era clients (today's ChatGPT and Claude) are answered with plain JSON; 2026-07-28 clients go through the SDK's modern handler.

  • One household per deployment for now. The schema carries household_id everywhere and tokens are household-scoped, so adding a second household is a data change plus a small sign-in page tweak, not a rewrite.

  • Timezone. Stored per household (seeded as Europe/Stockholm) so "tonight" resolves to the right date.

License

MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LinusU/mise-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server