mise-mcp
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, tokensInventory 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-textquantityText("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 |
| Members, today's date in the household timezone, default servings, all preferences. Call first. |
| Current items; filter by |
| Items expiring within N days (default 3). |
| Add one or more lots. |
| Change fields on existing lots ( |
| Record usage per item ( |
| Delete lots (thrown away, mistakes). |
| Meals in a date range (default next 7 days). |
| Create or replace meals for several days at once (by id or by date + type). |
| Move, rename, change status, swap (two updates in one call). |
| Hard delete. |
| Recipe library. |
| Durable planning preferences. |
| 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:
Static API token (Claude Code, MCP Inspector, curl, scripts):
npm run token:create -- Linus --label "Claude Code" --remote.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-resourcehandshake, 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 (theMISE_ACCESS_KEYsecret). 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 loginonce, so Wrangler can deploy.
npm install2. Create the D1 database
npx wrangler d1 create miseCopy 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:migrateRemote (production):
npm run db:migrate:remote4. 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:remoteFor 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_KEYLocally, copy .dev.vars.example to .dev.vars (already ignored by git) and
set the value there.
6. Local development
npm run devThe 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 D17. Deploy
npm run deploy8. 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" --remoteThe 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/inspectorTransport "Streamable HTTP", URL …/mcp, add an Authorization header with
Bearer <token>.
11. Test that it works
Ask the assistant:
"What food is in the fridge?"
"Add 1.2 kg chicken thighs to the fridge, expiring in 3 days, and a package of egg noodles to the pantry."
"Plan chicken noodle wok for tomorrow's dinner."
"Show the meal plan for the next week."
"We cooked the wok: used 450 g chicken and the whole package of noodles."
"How much chicken is left?"
"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 workerdDesign 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_ideverywhere 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
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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