Proposing recipe changes from chat
The Connector can do more than answer questions. It can also propose recipe changes that you review and apply in the Brikly web app. The AI assistant never touches your live recipes directly. Proposals land in the Recipe Drafts Inbox for your approval.
This is the chat-side companion to the inbox docs. Read both if you are setting this up for the first time.
When you would use it
The pattern shines when you want the assistant to make a structured change across one or more recipes, and you do not want to type the change into the web app by hand.
Examples that work well:
- "Swap dairy milk for oat milk on every drink that uses it. Use 10% more oat milk per drink to keep the body."
- "Add a salted caramel variant of the Latte at the same price as the Vanilla Latte."
- "Bump the brownie tray yield from 16 to 14 and update the per-slice cost."
- "Replace caster sugar with golden caster sugar on every bake and update the supplier mapping to match what we actually buy."
- "I want to add a pistachio cardamom bun." The assistant can propose the recipe even if you do not yet stock pistachio paste or ground cardamom, flagging both as pending sourcing for you to add at approval time.
The assistant works out the affected recipes, drafts the change for each one, and tells you what landed in the inbox. You review and approve in the web app.
What the assistant does behind the scenes
When you ask for a recipe change, the assistant follows a deliberate flow:
- Resolve the affected recipes. "Every drink that uses dairy milk" becomes a concrete list, often using the reverse lookup tool first. The assistant should confirm the list with you before going further.
- Cost each proposed change. For each recipe, the assistant builds the target spec and calls
costing_cost_recipe_specto get the cost, margin, and £/margin pp delta against the live recipe. This catches "I forgot the consumable cup" or "I got the unit wrong" before anything reaches the inbox. - Submit the draft. Once costs look right, the assistant calls
costing_propose_recipe_draft, which creates a pending draft in your inbox. The response includes the same cost preview the inbox will show. - Report back. The assistant tells you how many drafts landed, with the cost and margin deltas, and reminds you to review in the web app.
Steps 2 and 3 are required. The assistant is instructed to always cost a spec before proposing it. Unit conversions, consumables, sub-recipes, and VAT-correct margins are easy to get wrong by mental arithmetic, so the cost engine does the maths and the assistant quotes those numbers back to you.
A proposed spec carries no labour lines. When you approve a draft, the recipe's labour lines and its No labour needed setting are preserved exactly as they were, and the inbox says so with "Labour is unchanged by this proposal." Prep time is yours to set in the recipe builder. See Labour.
The three MCP tools
These tools are part of the costing.write scope. If you have already authorised the Connector for what-if scenarios, you have these too. If you have only granted read access, the assistant will tell you to reconnect Brikly from your assistant's connector settings so it asks for write access. Your existing connections are listed under Settings → Connections in the Brikly app, where you can also revoke them.
costing_cost_recipe_spec (read-only)
Costs a hypothetical recipe spec without persisting anything. The assistant uses this to iterate on a proposal ("try 220 ml of milk, what is the margin? now try 240 ml") before committing it as a draft.
If you pass a parent_recipe_id, the response includes the delta vs the live recipe.
Pure helper, no side effects. Same scope as the other read tools.
costing_propose_recipe_draft (write)
One tool, three actions, picked by an action argument: propose (the default) files a draft or revises a pending one, discard retracts a pending draft, and link_phantom_ingredient points a pending ingredient line at one you have since added. Until September 2026 the last two were tools of their own, costing_discard_recipe_draft and costing_link_phantom_ingredient. Nothing about what they do changed; if your assistant has an older cached tool list it may call the removed name once and get an unknown-tool error, then pick up the new list within about five minutes.
Filing a draft submits it to your inbox. It supports three operations:
- edit - change an existing recipe (most common).
- create - propose a brand-new recipe.
- duplicate - clone an existing recipe with edits (useful for seasonal variants).
Requires costing.write and a member-or-higher role, as do all three actions. Each draft has a short operator-facing summary and an optional rationale.
Ingredient lines on the proposed spec can be one of three shapes:
- Existing ingredient - the normal case, referencing a
tenant_ingredient_idfrom your pantry. - Sub-recipe - the line is itself another recipe (a "component"), referenced by
source_recipe_id. - Pending ingredient - a name and a purchase unit, no ID. Use this when the operator describes an ingredient they do not yet stock. The assistant must never invent a price for a pending ingredient. Operator sources it (link or create) at approval time.
Any ingredient line can also carry an optional section label (for example "For the sponges"), which groups the line under that heading on the recipe and on its recipe card. Sections are presentation only and never change what a recipe costs.
When a draft contains any pending ingredients, the cost preview's is_complete flag is false, proposed_cost_per_unit reflects only the costed lines, and margin fields are null. The assistant must quote partial cost as "X from costed lines so far" rather than as the final cost.
Revising a draft
The same tool revises a pending draft it already filed: the assistant passes the draft_id and leaves operation out. The draft_id does not change, so the operator sees one item evolving in their inbox, not a graveyard of discarded variants. This is what happens when you and the assistant work through a new product idea over several messages - the first message files a rough first pass, later messages tweak quantities, swap pending ingredients, add or remove lines, or sharpen the summary.
A proposed_spec on a revise is the full target state and replaces the stored one. It does not merge.
At least one of proposed_spec, summary, or rationale must be provided. A call carrying only the draft_id is rejected.
Structural identity is fixed at filing time: operation (edit / create / duplicate) and parent_recipe_id cannot change. If the operator wants to redirect the proposal at a different recipe, the assistant files a fresh draft instead, with no draft_id.
Only pending drafts can be revised. Approved, rejected, discarded, and superseded drafts are immutable.
Requires costing.write, the same as filing. Each revision is captured in an audit history on the draft, capped at the most recent 50 entries.
Discarding a draft
action="discard" with a draft_id retracts a pending draft the assistant proposed by mistake. Only pending drafts can be discarded from chat - approved, rejected, and superseded drafts are immutable once an operator has decided on them. The assistant can attach an optional reason, which lands in the audit trail beside the discard.
Members or higher can discard. Discarding a draft you proposed yourself is a benign action. Discarding one that is already discarded does nothing at all and still reports success, so an assistant retrying after a dropped connection cannot accidentally retract something twice.
Linking a pending ingredient
action="link_phantom_ingredient" points one pending ingredient line at an ingredient that already exists in your pantry. Use it when the assistant flagged something as pending sourcing and the real ingredient has since arrived - most often because you uploaded the invoice that created it.
The assistant passes the draft_id, the line_index of the pending line (read from the draft, never counted by hand), and the tenant_ingredient_id of the existing ingredient. The link mirrors what you would do in the inbox's resolution drawer: the original pending name, unit, and any notes stay on the draft, and the link is recorded alongside them, so the audit trail shows both what was proposed and what it became. Linking the same line to the same ingredient again does nothing; linking it to a different ingredient replaces the link and keeps the previous one in the draft's history.
It cannot create an ingredient. That stays with you, because the assistant has no supplier, price, or pack-size context to invent one responsibly. If the pending ingredient has no counterpart in your pantry yet, the assistant should tell you to add it (or upload the invoice) rather than offering to make one up.
costing_list_recipe_drafts (read-only)
Reads the inbox, in one of two modes.
Without a draft_id it lists pending drafts (or filters by status, operation). The assistant uses this to answer "what is in my inbox?" or to find a previous draft it proposed. It returns slim rows: id, recipe name, summary, cost delta, margin delta, stale flag, needs-sourcing flag, status. The needs-sourcing flag lets the assistant tell the operator at a glance how many of their drafts still have pending ingredients.
With a draft_id it returns the full payload for that one draft: proposed spec, base snapshot, cost preview (including is_complete and missing_ingredients), stale flag, pending ingredients with their current resolution state, review state. The assistant reads one draft this way when you ask "what did you put in that draft?" or "what is the status of my pistachio bun draft?". The list-mode arguments - the status and operation filters, and the limit / offset pagination - cannot be combined with a draft_id: the assistant is told to drop one or the other rather than have them silently ignored.
The assistant can read pending ingredient state across sessions. It can also resolve a pending line against an ingredient that already exists, with the link action described above; creating the ingredient is operator-only and happens in the web app inbox.
What the assistant cannot do
The split between assistant and operator is deliberate:
- Approve a draft. Only you can approve, and only from the web app's Recipe Drafts Inbox. If the assistant ever offers to "go ahead and apply that change", treat it as a hallucination.
- Reject a draft. Same rule. Rejection is an operator action.
- Push a what-if scenario live. Same separation as before - the assistant simulates, you push.
- Edit a recipe directly. The MCP server has no
costing_update_recipetool. The only way to change a live recipe through chat is propose, review, approve. - Create an ingredient. The assistant can flag an ingredient as pending sourcing on a draft, and it can link that line to an ingredient that already exists in your pantry, but it cannot bring a new one into being. Creating the ingredient happens in the inbox or the pantry, with you choosing the supplier and price.
- Quote a price for a pending ingredient. The assistant never invents an ingredient price. If it suggests "this paste typically runs around £18/kg" in agent notes, treat that as commentary, not data - the actual price you enter at create time is the only number Brikly trusts.
The 50 pending-draft cap
Each tenant can have at most 50 pending drafts at any one time. If the assistant tries to propose a 51st while 50 are already pending, the propose tool returns an error and the assistant tells you to clear some from the inbox before continuing.
In practice, this matters when an assistant tries to roll out a sweeping change across hundreds of recipes in one go. Ask for a smaller scope, or review what is already pending before going wider.
Stale drafts and what to do
If you change a live recipe after the assistant has proposed a draft against it, the draft may become stale. The web app inbox flags it. The assistant can also detect this on a subsequent costing_list_recipe_drafts call, with or without a draft_id - the is_stale flag in the response tells the assistant the draft may need re-review or replacement.
The assistant should not propose around a stale draft on its own. Resolve it in the inbox first (re-review, edit-then-approve, or discard), then ask the assistant to propose afresh if needed.
Audit and tenancy
- Every propose, revise, read, discard, and link call is logged with your user ID, the tenant ID, the draft ID, and the originating tool. You have a full audit trail.
- Cross-tenant access is impossible. A draft ID from another operator's tenant collapses to
not_found. - The assistant is told to surface impact and let you decide. If it proposes a specific price move or a big sweeping ingredient swap without you asking, push back and tell us - the prompt should not be steering you that way unprompted.
Example session
A typical exchange looks like this:
You: Swap dairy milk for oat milk on every drink that uses it. Use 10% more oat milk per drink to keep the body.
Assistant: I found 8 drinks that use dairy milk: Latte, Cappuccino, Flat White, Cortado, Mocha, Hot Chocolate, Chai Latte, and Matcha Latte. For each, I will replace the current dairy quantity with 10% more oat milk. Before I submit, here is the projected margin impact per drink: [table with current cost, projected cost, margin delta]. Should I go ahead and propose these as drafts?
You: Yes, but skip the Matcha Latte. We are running out of oat for matcha.
Assistant: Done. 7 drafts are now in your inbox at
/costing/drafts, each with the cost preview I quoted above. Approve them in the Brikly web app when you have a moment.
You then open the inbox, eyeball each draft, and approve or edit as needed. The assistant has done the structured work; you keep the final say.
Next steps
- Recipe Drafts Inbox - how to review and approve drafts in the web app.
- Example questions to try - other prompts that work well from chat.
- Troubleshooting - what to do when the Connector misbehaves.