plate-cost

plate-cost

Enables interactive recipe costing and menu-item pricing with deterministic math, including waste and margin calculations, and distinguishes whether figures came from the calculator or the model.

Category
Visit Server

README

plate-cost

An MCP App that costs recipes and prices menu items — an interactive calculator the model opens instead of doing the arithmetic itself.

Ask Claude "help me price my nasi goreng" and you get a working spreadsheet: ingredient rows, purchase prices, waste percentages, portion yield, a margin slider. Change a number, everything recalculates. Press Use these numbers and the model continues the conversation with figures you entered rather than figures it invented.

Built on MCP Apps (ext-apps).

plate-cost running in the ext-apps reference host

Running in the basic-host reference host from ext-apps — not Claude. The model proposed the recipe, the widget loaded it, and "Use these numbers" sent the corrected figures back into the conversation.

Why this is an app and not a tool

Costing is exploration, not a question. You do not know the answer you want until you have moved the portion size and watched the margin move. Chat is a bad interface for that, and a model doing the arithmetic in its head is worse — LLMs are unreliable at multi-step numeric work and completely reliable at sounding certain about it.

So the split is: the widget captures inputs, the server does the maths, the model does the talking.

The interesting part: knowing where the numbers came from

MCP Apps has an open gap — ext-apps#746: a tools/call from a widget and one the model decided to make are indistinguishable at the server. For most tools that does not matter. For a costing tool it decides whether you are doing arithmetic or laundering a guess.

mcp-app-attest closes it — extracted from this repo, which is its first consumer. The server mints a short-lived token each time it serves the app resource and substitutes it into the document; the widget echoes it back in _meta on every call. The model never sees the resource body, so it cannot produce the token. Calls are answered either way — the response says which it got:

{ "perPortion": 3300, "inputsFrom": "model" }

and when the numbers came from the model, the text reply says so:

Note: these figures came from the model, not from the calculator. Confirm the purchase prices with the user before relying on them.

Verified end to end against the reference host, not just in unit tests:

[provenance] inputsFrom=model  reason=missing  meta=null
[provenance] inputsFrom=app    meta={..."io.platecost/app-attestation":"XfqGVAiP…"}

What this is not. Not authorisation, and not proof a human typed anything. It distinguishes code paths, not identities. If a host copies the resource text into model context, the model can read the token — nothing here prevents that, it is the host's contract to keep. A compromised widget can send whatever it likes. Treat it as the difference between "typed into my form" and "produced by a language model", which is exactly what #746 asks for.

It has since been extracted into mcp-app-attest, which switched to signed tokens rather than stored ones — see the "what didn't work" note below for why.

Two things the domain gets wrong

Both are in src/costing.ts, both have tests.

Waste means buying more, not using more. Cost is used / (1 - wastePct), not used * (1 + wastePct). At 50% waste you buy double, not one and a half times. The naive version understates every trimmed ingredient you own.

Margin and markup are different numbers. Margin is (price - cost) / price; markup is (price - cost) / cost. A 60% markup is a 37.5% margin, and a 60% margin needs a 150% markup. priceForMargin takes margin — that is what "we want 65%" means in a kitchen — and returns both, so the gap is visible rather than assumed.

Also handled: unit conversion within mass and volume families, with a refusal across them (kgl needs a density you have not given), per-ingredient cost share, and overhead applied per portion rather than per batch.

Try it

npm install
npm run build      # bundles the widget to a single inlined HTML
npm test           # 21 tests
npm run dev        # widget standalone in a browser, no host

Point an MCP client at server.ts. Outside a host the attestation placeholder is never substituted, so calls are labelled model — which is the honest answer.

What didn't work

  • A stateless HTTP transport. The first version built a fresh server per request, which is fine for stateless tools and silently breaks this one: the attestation was minted by a server instance that no longer existed when the widget called back, so every widget call was rejected as unknown. The in-memory tests could not see it — they share one instance. Only running against a real host exposed it.
  • Blaming the SDK. Before finding the above, the obvious suspect was _meta being stripped in transit. It is not: the reference host forwards it intact through the sandbox proxy. Worth knowing, because the whole mechanism depends on hosts doing so and the spec does not require it.

Status

v0. Two tools (cost-recipe, price-portion), one widget, 21 tests.

Not built: persistence between conversations, multi-recipe comparison, and anything touching the namespacing problem in #753 / #745callServerTool uses bare tool names, so this will break behind an aggregator. That is the next thing worth solving and it is not solved here.

MIT.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured