Skip to main content
Glama

Server Details

Connect your espresso routine to your AI assistant. This MCP server allows LLMs to act as a personal coffee coach by accessing your shot logs, grinder settings, and active bean inventory. Use it to automatically calculate extraction adjustments, track remaining coffee supply, and lock in winning recipes directly from your chat session.

Ownership verified
Status
Healthy
Uptime
99.9% over 79 days
Last Tested
Transport
Streamable HTTP · MCP 2025-06-18
URL
ChatGPT
ShotPulled
Claude
–

TDQS

A3.5/5.0

Scored across 44 tools

Disambiguation4/5

Most tools target a distinct resource/action, and descriptions draw clear lines between close pairs such as diagnose_shot vs diagnose_preview and update_bean vs update_bag. With 44 tools there are still some boundary judgments, especially get_dial_state vs suggest_next_step, list_maintenance_due vs the maintenance blocks in list_grinders/list_machines, and log_recommendation vs suggest_next_step.

Naming Consistency4/5

Almost all names use snake_case with a predictable verb_noun pattern such as list_*, register_*, update_*, set_*, and log_*. Minor inconsistencies remain: bean vs coffee appears across list_beans/register_coffee/update_bean, and grinder_math/kb_changelog are verbless outliers.

Tool Count2/5

44 tools is far above the well-scoped 3-15 range and beyond the heavy 16-25 range, spanning beans, bags, grinders, machines, scales, waters, recipes, shots, maintenance, and knowledge-base operations. The breadth reflects a real domain, but the count still creates substantial selection overhead.

Completeness4/5

Core lifecycles are well covered: beans/bags, equipment, shots, recipes, maintenance, and KB operations have list/register/update/log/diagnose/archive coverage. Minor gaps exist, such as recipes having list+lock but no explicit unlock/delete/update, and programs being registerable/overwritable but not directly listable or deletable; agents can mostly work around these.

Available Tools

44 tools
compute_ageCompute coffee ageA
Read-onlyIdempotent
Inspect

Compute a bag's age in days off roast and its resting/staling verdict, for the coffee's current bag by default or for the bag named.

NameRequiredDescriptionDefault
bag_idNoA bag of that coffee (from list_beans' bags[]); null = its current bag
bean_idYes

Output Schema

NameRequiredDescription
bag_idYes
bean_idYes
verdictYes
age_daysYes
warningsYes
roast_dateYes
rest_windowYes
verdict_codeYes
days_off_roastYes
grams_remainingYes
days_since_openedYes
effective_age_daysYes
frozen_days_excludedYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare that this is a read-only, idempotent, non-destructive, closed-world operation. The description adds useful behavioral context by explaining that it computes age off roast and a resting/staling verdict, and that it resolves to the current bag unless a bag is specified.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no filler. Every clause contributes either the computation performed, the verdict returned, or the bag-selection behavior.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple, an output schema exists, and annotations cover its safety profile, so the description does not need to explain return values or side effects. It is nearly complete, with only minor room to clarify the required bean_id parameter.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 50%: bag_id is documented in the schema, but bean_id has no schema description. The description partially compensates by implying bean_id identifies the coffee and clarifying that bag_id defaults to the current bag, but it does not explicitly explain the required bean_id parameter or its role.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource: it computes a bag's age in days off roast and its resting/staling verdict. It also distinguishes the default current-bag behavior from an explicitly named bag, so an agent can identify the tool's function without inspecting the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear operational context: use the coffee's current bag by default or target a named bag. It does not explicitly state when to prefer this tool over siblings, but no sibling provides an equivalent age/verdict computation, so the usage is sufficiently clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_shotDelete a shotA
DestructiveIdempotent
Inspect

Delete a logged shot from history. Restores the bean's remaining weight (which is derived from logged doses). Hard delete: there is no undo. To fix a mistake on an otherwise-valid shot, prefer update_shot over delete-and-relog.

NameRequiredDescriptionDefault
shot_idYesID of the shot to delete

Output Schema

NameRequiredDescription
statusYes
messageYes
deleted_shot_idYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, so the irreversibility itself is structured data — but the description adds real information beyond it: the bean's remaining weight is restored and is derived from logged doses. That side effect could not be inferred from annotations or schema. Minor gap: the idempotentHint=true annotation sits slightly awkwardly with a hard delete (a second call would target a missing row), and the description does not address repeat-call behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action, then the non-obvious side effect, then the routing rule. Nothing is wasted and nothing important is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists so return values need not be explained, and annotations cover the safety profile. Combined with the side-effect and alternative-tool guidance, an agent has everything needed to call this correctly and safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is documented as 'ID of the shot to delete'. The description adds no format, sourcing, or lookup guidance (e.g. where to get shot_id) beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Delete a logged shot from history') and immediately separates it from the sibling that handles corrections ('prefer update_shot over delete-and-relog'). An agent can distinguish it from update_shot without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit when-to-use condition: only for removing a shot from history, not for fixing a mistake on a valid shot — in which case update_shot is named as the alternative. This is exactly the routing guidance the dimension asks for.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

diagnose_previewPreview a diagnosisA
Read-onlyIdempotent
Inspect

Sandbox-only diagnosis, writes nothing (no verdict, no recommendation-trail entry). Two modes: pass shot_id to dry-run a LOGGED shot (optionally overriding its sensory_tags, for the "what would this read as?" preview; the shot's own bean and its age at pulled_at are used), or pass the full metric set (bean_id, grinder_id, machine_id, grind_label, dose_g, yield_g, time_s, source) for a hypothetical shot. Identical output shape to diagnose_shot, including bean_context.

NameRequiredDescriptionDefault
dose_gNoDose in grams
sourceNoGrinder position source. Affects G1 warning (raw mode only)
time_sNoExtraction time in seconds
bean_idNoCoffee bean ID (must belong to this account)
shot_idNoPreview a logged shot by ID (dry-run; ignores the metric params below)
yield_gNoYield in grams
grinder_idNoGrinder ID (must belong to this account)
machine_idNoMachine ID (must belong to this account)
grind_labelNoGrinder setting label, e.g. "1.1.3"
sensory_tagsNoSensory observations, e.g. ["sour","bitter"]. With shot_id: replaces the stored tags for this preview (omit to use stored; [] = none).

Output Schema

NameRequiredDescription
factsYes
categoryYes
warningsYes
hypothesesYes
kb_versionYes
bean_contextYes
warning_codesYes
one_variable_onlyYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only/idempotent/non-destructive, and the description adds real value on top: it names exactly what is not produced (no verdict, no recommendation-trail entry), clarifies which bean and which age snapshot are used in shot_id mode, and defines the sensory_tags override semantics including the empty-array case.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the key fact (writes nothing), then the two modes in dependency order. Dense and mostly earned, though the single long parenthetical sentence could be split for scanability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need no explanation, yet the description still points at parity with diagnose_shot including bean_context. Mode selection, side-effect profile, and parameter grouping are all covered for a 10-parameter, all-optional tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so a 3 is the floor, but the description earns more: with zero required parameters declared, saying that the hypothetical mode needs the full metric set (bean_id, grinder_id, machine_id, grind_label, dose_g, yield_g, time_s, source) tells the agent which subset to supply. It adds no format or unit detail beyond the schema, which is fine given full coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (sandbox-only diagnosis preview) and immediately names its sibling diagnose_shot as the output-equivalent, so the agent can triangulate its role without opening another schema. The two operating modes are named concretely.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly explains the two invocation paths and their conditions: shot_id to dry-run a logged shot, or the full metric set for a hypothetical shot. It stops short of an explicit when-not statement (e.g. 'use diagnose_shot to actually record a verdict'), leaving that contrast implied by naming the sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

diagnose_shotDiagnose a shotA
Read-onlyIdempotent
Inspect

Evaluate shot metrics to provide ranked hypotheses (C6) and warn on flip-flops (G2) or fatigue (G6). Resolves the SHOT's own bean (age computed at the shot's pulled_at), never the active profile, and echoes it as bean_context, so diagnosing an older or differently-filed shot is always safe. The engine reads metrics and sensory tags, NOT free-text notes, so make sure taste feedback is recorded as sensory_tags on the shot (via log_shot or update_shot) before diagnosing; otherwise an in-range shot that tastes bad will come back "balanced".

NameRequiredDescriptionDefault
shot_idNoOptional shot ID; defaults to last pulled shot.

Output Schema

NameRequiredDescription
factsYes
categoryYes
warningsYes
hypothesesYes
kb_versionYes
bean_contextYes
warning_codesYes
one_variable_onlyYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds real substance beyond them: bean resolution semantics, the echo of bean_context, and the fact that the engine ignores free-text notes. It does not discuss output shape or confidence limits, but the added context is well above the annotation baseline.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the core function and warnings, then scope, then the prerequisite. The rule codes (C6/G2/G6) are opaque without KB context and occupy space that could be plainer, but there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description supplies the missing behavioral context: bean resolution, the sensory_tags requirement, and what warnings to expect. Adequate for a zero-required-param read tool, with only the sibling routing left implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter's default (last pulled shot) is documented in the schema itself. The description adds no independent detail about shot_id, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: evaluate shot metrics and return ranked hypotheses plus flip-flop/fatigue warnings. The scope clarification (resolves the shot's own bean, not the active profile) helps separate it from a preview-style diagnosis, but it never explicitly names diagnose_preview as the alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete prerequisite: sensory feedback must be recorded as sensory_tags on the shot via log_shot/update_shot before calling, otherwise bad-tasting in-range shots come back 'balanced'. That is a clear use condition, though it stops short of stating when to prefer this over diagnose_preview.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_dial_stateGet current dial stateA
Read-onlyIdempotent
Inspect

Retrieve current dialing state, active context, recent shots, recommendations, and locked recipes.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
recent_shotsYes
active_coffeeYes
locked_recipeYes
active_grinderYes
active_machineYes
active_programYes
maintenance_summaryYes
recent_recommendationsYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish that this is a read-only, idempotent, non-destructive, closed-world operation. The description adds useful behavioral context by disclosing the aggregate contents returned, including active context, recent shots, recommendations, and locked recipes, though it does not discuss freshness or limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no wasted words. The action and the returned content are presented immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a zero-parameter, read-only aggregate tool with an output schema, so the description does not need to document parameters or return structure. It sufficiently states what the tool retrieves and what categories of data the agent should expect.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there are no parameter semantics for the description to explain. Per the baseline for zero-parameter tools, a 4 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb (Retrieve) and a clear resource (current dialing state), then enumerates the bundled content: active context, recent shots, recommendations, and locked recipes. It distinguishes the tool from simple list tools by framing it as an aggregate current-state read, though it does not explicitly name sibling alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the phrase 'current dialing state' and the listed outputs, suggesting the tool is for getting a consolidated dialing overview. However, the description gives no explicit when-to-use guidance, no prerequisites, and no comparison to siblings such as list_shots or suggest_next_step.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_kb_versionGet knowledge base versionA
Read-onlyIdempotent
Inspect

Retrieve the current version of the knowledge base.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
kb_versionYes

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, so the safety profile is fully covered. The description adds no extra behavioral context beyond the basic retrieval action, but it does not contradict the annotations either. Given the low bar enabled by rich annotations, a neutral score of 3 is appropriate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured sentence that states the exact operation and target. There is no redundant or filler content, and the key information is front-loaded. It earns its place by being perfectly compact and informative at a basic level.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity, an output schema exists to define the return value, and annotations cover the operational behavior, nothing critical is missing from the description. It could have added a hint about what 'knowledge base version' refers to, but for a zero-parameter read-only getter, the description is sufficiently complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there is no parameter information to explain. Per the rubric, a 0-parameter tool gets a baseline of 4; the description does not need to compensate for any schema gaps because there are no parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'Retrieve' and identifies the resource as 'the current version of the knowledge base,' making the tool's purpose immediately clear. It does not differentiate itself from siblings, but no sibling appears to handle the same operation, so the lack of explicit distinction is not a significant gap.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The usage context is implied by the name and description: an agent would naturally use this to get the knowledge base version. However, the description gives no explicit guidance about when to prefer this over related tools like kb_changelog or get_stats, nor any conditions under which it should not be used.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_maintenance_guidesGet maintenance guidesA
Read-onlyIdempotent
Inspect

Step-by-step maintenance guides: tools, supplies, steps and warnings, in our own words with the maker's documents as sources. With no arguments, every guide for the user's current grinders and machines (and the water filter fitted in each). With guide_ids, those guides; the ids come from maintenance.tasks[].guide.id in list_grinders and list_machines. Ids that match no guide are listed under missing.

NameRequiredDescriptionDefault
guide_idsNoOptional: the guides to fetch. Omit for every guide the user's equipment can use

Output Schema

NameRequiredDescription
guidesYes
missingYes
catalog_versionYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only, idempotent, non-destructive, closed-world behavior. The description adds meaningful operational context: default scope, id provenance, unmatched-id reporting under 'missing', and that guide content is synthesized from maker documents.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three front-loaded sentences with no filler; each sentence carries either mode selection, scope, id provenance, or edge-case behavior. Dense but appropriately sized for a retrieval tool with default and filtered modes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description need not explain the return shape. It covers content type, default scope, id source, and unmatched-id handling, while annotations carry the safety profile, so nothing material is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description still adds substantial meaning beyond the schema: guide_ids should come from maintenance.tasks[].guide.id in list_grinders and list_machines, omitting the parameter fetches all guides for current equipment, and unmatched ids are reported under 'missing'.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource, enumerates the guide contents (tools, supplies, steps, warnings), and explains both invocation modes. It clearly distinguishes this retrieval tool from scheduling/due-list siblings by naming the sibling tools only as id sources.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the no-argument behavior (all guides for current grinders, machines, and fitted water filters) and the guide_ids behavior, including where ids come from. It does not explicitly compare against siblings such as list_maintenance_due or log_maintenance, nor does it state when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_ruleGet a dialing ruleA
Read-onlyIdempotent
Inspect

Retrieve the detailed text of a specific rule from the knowledge base by its rule ID.

NameRequiredDescriptionDefault
rule_idYesThe ID of the rule to fetch, e.g. CAT.DARK

Output Schema

NameRequiredDescription
textYes
rule_idYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds minor behavioral context by promising 'detailed text' as the return content, but does not disclose error/not-found behavior. No contradiction exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with no filler; the core operation, resource, and identifier are stated immediately. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, idempotent, read-only lookup with one fully documented parameter and an output schema, the description provides sufficient context. It does not explain what kinds of rules exist or where rule IDs originate, but those are not necessary for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter rule_id is fully described in the input schema (100% coverage), including an example value 'CAT.DARK'. The description only reinforces that lookup is by rule ID, adding no significant meaning beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies a specific action ('Retrieve the detailed text'), a specific resource ('a specific rule from the knowledge base'), and the lookup key ('by its rule ID'). It is clear, though it does not explicitly distinguish itself from sibling getters such as get_dial_state or get_kb_version.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage: when you have a rule ID and need the rule text. However, it provides no explicit guidance on when not to use this tool or which sibling tool to prefer for related lookups.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_statsGet statisticsA
Read-onlyIdempotent
Inspect

Get compact flat usage stats for a grinder, machine, or bean.

NameRequiredDescriptionDefault
idYesThe entity ID
scopeYesThe stats scope

Output Schema

NameRequiredDescription
brewsYes
last_beanNo
last_usedNo
grams_groundNo
effective_ageNo
beans_consumedNo
grams_consumedNo
grams_remainingNo
last_grind_settingNo

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, covering the safety profile. The description adds that stats are 'compact flat', which hints at the output shape, but it does not disclose aggregation details, time range, or any other behavioral nuances beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, compact sentence that front-loads the essential information. Every word contributes: the action, the output type, and the supported scopes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter read-only tool with an output schema and comprehensive annotations, the description is complete enough. The schema covers parameters, annotations cover behavior, and the output schema covers return structure, leaving no critical gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents both parameters fully. The description adds minimal parameter meaning beyond echoing the scope enum ('grinder', 'machine', 'bean') and the notion of usage stats, so a baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the action ('Get'), the resource type ('usage stats'), and the supported scopes ('grinder, machine, or bean'). This distinguishes it from sibling getters like get_dial_state and get_rule, which target different kinds of information.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use the tool: when you need usage statistics for one of the three entity types. However, it does not explicitly contrast with alternatives or state when not to use it, such as when listing entities or retrieving state information.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

grinder_mathGrinder mathB
Read-onlyIdempotent
Inspect

Determine steps/clicks difference and directions between setting labels (C3).

NameRequiredDescriptionDefault
to_labelYes
from_labelYes
grinder_idYes

Output Schema

NameRequiredDescription
stepsYes
directionYes
step_unitYes
burr_gap_travel_micronsYes
relative_coarseness_noteYes

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, and the description's 'Determine' is consistent with a safe computation. It adds that the result is a steps/clicks difference and direction, but does not reveal additional behavioral nuance such as label assumptions or failure behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence with no filler, and the key operation is front-loaded. The unexplained 'C3' parenthetical costs it a top score because it is cryptic rather than clarifying.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only math utility with an output schema, the high-level purpose is enough for selection, but the agent must guess the labeling convention and why grinder_id is required. Combined with no usage routing, the definition is workable but not fully self-sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description must carry the meaning, but it only loosely connects from_label/to_label as 'setting labels' and never explains the label syntax or the grinder_id parameter. It also doesn't state the mapping between labels and steps/clicks, which is the core semantic the agent needs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly identifies a calculation: it determines the difference/direction in steps/clicks between two setting labels, so an agent knows what the tool computes. It does not explicitly distinguish itself from siblings like set_grinder_position, and the parenthetical 'C3' is unexplained.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides no guidance on when to call this rather than a sibling such as set_grinder_position or suggest_next_step. It neither states prerequisites (e.g., a registered grinder) nor when the math should be used, so an agent must infer from context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

kb_changelogKnowledge base changelogA
Read-onlyIdempotent
Inspect

Retrieve the changelog showing updates and version differences for the knowledge base.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
changelogYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description is consistent with the annotations (readOnlyHint=true, idempotentHint=true, destructiveHint=false). It adds mild context by specifying the changelog content (updates and version differences), but it does not describe ordering, scope, or any additional behavioral detail. Given the annotations already cover safety, this is adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One clear sentence with no filler. The main action and content are front-loaded, and every word adds meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, parameterless read-only tool with rich annotations and an output schema present, the description is sufficient. An agent can correctly infer what the tool returns and that invoking it is safe and non-mutating.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there is no parameter semantics to explain. The baseline of 4 applies here. The schema coverage is 100% trivially, and the description does not need to compensate for undocumented parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Retrieve') and resource ('changelog') and clarifies what it contains ('updates and version differences for the knowledge base'). It distinguishes from the sibling get_kb_version by indicating full version history rather than the current version.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The intended use is reasonably clear from the description: retrieve knowledge-base changelog/history. However, it does not explicitly mention when to use this tool over get_kb_version or other knowledge-related tools, nor does it state any exclusions. The usage context is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_beansList coffeesA
Read-onlyIdempotent
Inspect

List the coffees registered for the account, with status filter. Each coffee carries its bags[] (every purchase, with its own freshness and grams left) and current_bag_id; the row's top-level roast_date, state, grams_remaining and age fields are the current bag's. The id on each row is the number this account knows that coffee by, counting from 1: safe to show, and what other tools expect back; bag ids are a separate series.

NameRequiredDescriptionDefault
statusNoFilter by status: current, archived, or all. Defaults to current.

Output Schema

NameRequiredDescription
beansYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare this as a safe, read-only, idempotent operation. The description adds useful behavioral context beyond annotations by explaining that each coffee carries bags[], current_bag_id, and that top-level roast_date/state/grams_remaining/age fields belong to the current bag.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, then adds relevant data-model and ID clarifications. It is somewhat dense for a one-parameter list tool, but every sentence provides useful context rather than repeating structured fields.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity, full schema coverage, existing output schema, and annotations covering safety, the description is complete enough for correct invocation. It additionally resolves the potentially confusing distinction between coffee ids and bag ids.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the status enum is fully documented in the schema, so the schema carries the parameter semantics. The description only restates that there is a status filter, without adding format or default behavior beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: listing coffees registered for the account, with a status filter. It clearly separates coffee rows from bag data, but it does not explicitly distinguish this tool from siblings such as register_coffee or update_bean.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'List the coffees registered for the account' with a status filter, so an agent can infer this is for browsing coffees. However, there is no explicit when-to-use guidance, no exclusions, and no named alternatives for cases such as creating or updating coffees.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_grindersList grindersA
Read-onlyIdempotent
Inspect

List grinders registered for the account, with status filter. The id on each row is the number this account knows that record by, counting from 1: safe to show, and what other tools expect back. Each row carries its maintenance block: every task with its class (S schedule, H habit, Y symptom, A the machine signals it, T technician, N never, U unstated, E event), due state, and the guide to follow.

NameRequiredDescriptionDefault
statusNoFilter by status: current, archived, or all. Defaults to current.
maintenanceNoHow much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words (quote, source, alternates, the interval as written).

Output Schema

NameRequiredDescription
grindersYes

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive, so safety is covered. The description adds genuinely useful non-obvious behavior: the `id` is a per-account ordinal counting from 1, safe to display, and the handle other tools expect back. It explains the row contents, but not pagination or ordering.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first clause, followed by the id contract and then the row shape. The class-code list (S/H/Y/A/T/N/U/E) is dense but each token is load-bearing for interpreting output, so it earns its space. Still one long sentence in the middle section.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values don't need full explanation, yet the description still clarifies the non-obvious id semantics and class-code vocabulary, which are the main interpretation hazards. It omits ordering and pagination, which is the remaining gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both `status` and `maintenance` are fully documented in the schema. The description restates 'status filter' and the maintenance block contents but adds little parameter-level detail beyond the schema, which is the baseline-3 case.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with scope: 'List grinders registered for the account, with status filter.' An agent knows exactly what it returns. It does not, however, distinguish itself from siblings like list_maintenance_due or get_maintenance_guides, which also surface maintenance information.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is only implied by 'with status filter' and by the maintenance compaction choice. There is no explicit when-to-use or when-to-prefer-a-sibling guidance, no mention of list_maintenance_due for due-only queries, and no exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_machinesList espresso machinesA
Read-onlyIdempotent
Inspect

List machines registered for the account, with status filter. The id on each row is the number this account knows that record by, counting from 1: safe to show, and what other tools expect back. Each row carries its maintenance block, researched per model: every task with its class, due state, what the maker forbids, and the guide to follow. settings lists the selectors its model has and where each stands (null: unknown model, or none).

NameRequiredDescriptionDefault
statusNoFilter by status: current, archived, or all. Defaults to current.
maintenanceNoHow much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words (quote, source, alternates, the interval as written).

Output Schema

NameRequiredDescription
machinesYes

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive behavior, so the bar is lower. The description nonetheless adds genuinely useful behavioral context: the `id` is account-relative and counts from 1 (safe to display, and what other tools expect back), and `settings` is null when the model is unknown or has no selectors. Those are non-obvious traits an agent would otherwise have to discover by trial.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose and the ID semantics are front-loaded, and every sentence conveys information about the payload that the agent would need. The second and third sentences are dense and slightly overloaded, but no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, yet the description still covers the two traps: account-relative IDs and null settings. Combined with annotation coverage, an agent has enough to call this correctly; only the relationship to sibling listing tools is left unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters are enums fully documented in the schema, so the baseline is 3. The description mentions the status filter but adds no syntax or default information beyond the schema, and the `maintenance` parameter's compact/full distinction is left entirely to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb and resource: 'List machines registered for the account, with status filter.' That is unambiguous and distinguishable from register_machine and update_machine. It does not, however, explicitly separate itself from list_maintenance_due or list_grinders, which is what a 5 would require.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the listing and filtering use case via 'with status filter' and describes what the rows contain, but never says when to choose this tool over list_maintenance_due or when a full maintenance block is warranted. Usage must be inferred from the payload description rather than being stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_maintenance_dueList maintenance dueA
Read-onlyIdempotent
Inspect

What maintenance is due across the user's current grinders and machines, most urgent first. Use it for "is anything due?". Only tasks with an interval compete: a task the machine counts and signals itself, one done by habit or when a symptom shows, and one the maker advises against never appear. Each item says how far along it is (ratio, done of every, on which basis), whether it is coming up (soon), and whether the interval is the user's, the maker's or ShotPulled's default.

NameRequiredDescriptionDefault
statusNodue (default): due or overdue, and not muted. all: every scheduled task, muted ones last

Output Schema

NameRequiredDescription
itemsYes
computed_atYes
catalog_versionYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish read-only, idempotent, non-destructive behavior, so the bar is lower. The description nonetheless adds real behavioral context beyond the annotations: only interval-based tasks are considered, and machine-counted, habit-driven, and maker-discouraged tasks never appear — a meaningful scoping rule an agent could not guess.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first sentence, followed by usage context and then item-level semantics. Three sentences with little waste, though the final sentence overlaps with what the output schema presumably documents.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With safety fully covered by annotations, a single enum parameter, and an output schema, the description supplies the pieces the structured fields cannot: ordering, inclusion/exclusion rules, and the provenance of each interval (user, maker, or ShotPulled default). Complete enough for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single enum parameter carries its own explanation (due = due/overdue and not muted; all = every task, muted last). The description never references the status parameter, so it adds no meaning beyond the schema; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific query over a specific resource — what maintenance is due across the user's current grinders and machines — plus output ordering (most urgent first). It is clearly distinguishable from sibling tools like set_maintenance_schedule, log_maintenance, and get_maintenance_guides, which write or reference guidance rather than report current status.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Use it for 'is anything due?'" gives a concrete triggering question, and the exclusion rules clarify what the answer will and will not contain. However, it never names an alternative tool (e.g. get_maintenance_guides for how-to, log_maintenance for recording work), so the agent must infer when another sibling is the better call.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_recipesList locked recipesC
Read-onlyIdempotent
Inspect

List recipes (all or filtered by equipment and status). The id on each row is the number this account knows that record by, counting from 1: safe to show, and what other tools expect back.

NameRequiredDescriptionDefault
statusNoFilter by status: current, archived, or all. Defaults to current.
bean_idNo
grinder_idNo
machine_idNo

Output Schema

NameRequiredDescription
recipesYes

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive, and closed-world, so the safety profile is covered. The description adds one genuinely useful behavioral fact — the `id` is an account-local sequential number safe to display and usable as a key elsewhere — but says nothing about ordering, pagination, or empty results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, front-loaded with the core action. The second sentence is convoluted ("the number this account knows that record by, counting from 1") where "account-local sequential ID" would say it faster.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return shape needn't be explained, and annotations cover safety. But with three of four parameters undocumented and no statement of filter-combination semantics or default behavior, the description does not close the gaps the structured data leaves.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 25%: `status` is documented in the schema but bean_id, grinder_id, and machine_id have nothing. The phrase "filtered by equipment and status" implies those three IDs are equipment filters, but never says what each one selects or how multiple filters combine (AND vs OR).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("List recipes") plus the two filtering axes (equipment and status), which is enough to distinguish it from mutation siblings like lock_recipe or update_shot. It stops short of differentiating it from other list_* tools (list_shots, list_beans), which share the same shape.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance and no named alternative. The only usage signal is the implicit hint that the returned `id` is "what other tools expect back," which suggests chaining but never states the condition that selects this tool over a sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_scalesList scalesA
Read-onlyIdempotent
Inspect

List registered scales for the account. The id on each row is the number this account knows that record by, counting from 1: safe to show, and what other tools expect back.

NameRequiredDescriptionDefault
statusNoFilter by status: current (unarchived), archived, or all. Defaults to current.

Output Schema

NameRequiredDescription
scalesYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false, so the description does not need to restate safety. It adds valuable behavioral context by explaining that the id is a stable, user-facing number counting from 1 and is what other tools expect back.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the purpose and then a useful clarification about the id field. Every sentence contributes without repetition or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple one-parameter schema, rich annotations, and an existing output schema, the description is complete enough for correct invocation. It covers purpose and the non-obvious id semantics without needing to explain return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, including a fully documented status enum with default behavior, so the schema carries parameter semantics. The description adds no additional detail about the status parameter, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ("List registered scales") and scopes it to the account, clearly distinguishing it from register_scale, update_scale, and the other list_* siblings by resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It conveys the basic purpose but gives no explicit guidance on when to choose list_scales over alternatives, no prerequisites, and no exclusions. The note about id semantics is useful but does not tell an agent when to invoke the tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_shotsList shotsA
Read-onlyIdempotent
Inspect

List shot history with support for pagination and filtering. Every shot carries a derived taste_pending flag (1 = logged with no sensory tags, no rating, and no tasted flip: the tasting is still owed and can be backfilled with update_shot).

NameRequiredDescriptionDefault
limitNoNumber of records to return (1-500)
offsetNoOffset for pagination
filtersNo

Output Schema

NameRequiredDescription
shotsYes
totalYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare a safe, idempotent read, so the description correctly spends its words elsewhere: it discloses that taste_pending is a derived flag and defines precisely what makes it 1. It does not state default sort order or whether filtering combines with AND, which are meaningful behavioral details for a list tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with capability and then the derived-field explanation. The taste_pending parenthetical is dense but every clause adds information an agent cannot get from the name alone; no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't explain return values, and annotations cover the safety profile. What remains thin is ordering and filter-composition behavior, but the core call-correctly information is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%, so the schema already carries most parameter meaning, including a taste_pending definition nearly identical to the description's. The description adds the actionable consequence (backfill via update_shot) but says nothing about how limit/offset behave at boundaries or how multiple filters compose. Baseline 3 fits.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("List shot history") plus the two capabilities that matter (pagination, filtering). It does not distinguish itself from neighbors like get_stats or diagnose_shot, but the resource is concrete enough that an agent can identify the tool's job.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context for use (history retrieval with pagination/filtering) and explicitly names update_shot as the path for backfilling an owed tasting, which is real routing guidance. No exclusion rules tell the agent when to prefer get_stats or diagnose_shot instead, keeping it below a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_watersList water recipesA
Read-onlyIdempotent
Inspect

List registered waters for the account. The id on each row is the number this account knows that record by, counting from 1: safe to show, and what other tools expect back.

NameRequiredDescriptionDefault
statusNoFilter by status: current (unarchived), archived, or all. Defaults to current.

Output Schema

NameRequiredDescription
watersYes

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish that this is a safe, read-only, idempotent operation, so the bar is lower. The description adds useful return-value context: each row's id is account-scoped, starts at 1, is safe to expose, and is what other tools expect. It does not cover ordering or pagination, but adds meaningful behavioral detail beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences, front-loaded with purpose and followed by a relevant note about IDs. There is no wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with full schema coverage, strong annotations, and an output schema, the description supplies what is needed and adds an important note about ID semantics. Nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the only parameter is an enum with a clear description and default. The tool description does not add further meaning about the status filter, so baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb and resource: 'List registered waters for the account.' It is easy to distinguish from register_water or update_water, but it does not explicitly differentiate itself from other list_* siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It says what the tool does but gives no guidance on when to use it versus related tools such as list_recipes or the register/update water tools. There are no exclusions, prerequisites, or alternative-selection hints.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

lock_recipeLock a recipeA
DestructiveIdempotent
Inspect

Lock a successful shot as the reference dialing recipe for this equipment profile. It overwrites the recipe already locked for the same coffee, grinder, machine, program and drink intent, which keeps its recipe_id.

NameRequiredDescriptionDefault
drink_intentYes
from_shot_idYes

Output Schema

NameRequiredDescription
statusYes
messageYes
recipe_idYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds the critical specifics beyond that: it overwrites only the recipe matching the same coffee/grinder/machine/program/drink-intent tuple, and preserves the existing recipe_id. That explains exactly what is destroyed and how identity is retained, which the annotations alone cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no filler, and the definition of the action is front-loaded before the overwrite rule. Every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't explain return values, and the annotations cover the safety profile. It covers the mutation effect well; only minor gaps remain, such as failure behavior or why drink_intent (straight/milk) is a required selector.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema coverage the description must carry the load, and it does: it frames from_shot_id as the 'successful shot' source and names the composite key (coffee, grinder, machine, program, drink intent) that determines which record drink_intent selects. This meaningfully exceeds the bare enum/integer schema, though from_shot_id's exact semantics remain only implied.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (lock) and resource (recipe) and defines what locking means: promoting a successful shot to the reference dialing recipe for the equipment profile. This clearly separates it from list_recipes, update_shot, or log_shot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Lock a successful shot' implies you should have a successful shot to promote, which is implied usage context. However, it never states when-not to use it or points to any alternative (e.g., list_recipes or update_shot), leaving the routing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

log_maintenanceLog maintenanceAInspect

Log a cleaning or maintenance job against a piece of equipment. A task that takes the thing apart opens a calibration epoch, and every recipe locked before it is read through that epoch's offset. Record the offset here if you counted the clicks while reassembling, which is the one moment anybody actually knows it. Any task in the maintenance vocabulary can be logged against a grinder or machine it belongs to, including one the maker advises against, because the log records what was done. Read each piece of equipment's tasks, their intervals and their due state from list_grinders and list_machines.

NameRequiredDescriptionDefault
noteNoOptional: what you did, and anything that surprised you
taskYesWhich maintenance job was performed
detailsNoFields specific to this task. Which ones a task takes is published as details_fields on each task; a burr clean takes none
evidenceNoOne line recording what the measurement rested on, e.g. "Counted 6 clicks coarser at reassembly."
guide_idNoOptional: the id of the maintenance guide that was followed. Must be a guide for this task
equipment_idYesThe ID of that piece of equipment
offset_valueNoThe correction, if you measured it now: clicks to ADD to a pre-clean reading to get the same grind today, signed along the collar's own number direction. Omit it and the epoch stays unmeasured, which is a state the app shows rather than a zero it assumes
performed_atNoWhen it was done (ISO 8601 or YYYY-MM-DD HH:MM:SS, UTC). Defaults to now; backdating is fine
disturbs_zeroNoWhether this job disturbed the reference the stored numbers are read against: for a burr clean, whether the burrs actually came out. Defaults to the task's own answer; send 0 for a wipe-down that left the collar alone
offset_sourceNoHow the offset was arrived at: pull (a reference shot), counted (clicks counted at reassembly), typed (asserted directly)
equipment_kindYesWhich kind of equipment this job was done on

Output Schema

NameRequiredDescription
entryYes
dedupedYes
messageYes
entry_idYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantial behavior beyond annotations: a disassembly task opens a calibration epoch, prior locked recipes are reinterpreted through that epoch's offset, and omitting offset_value leaves the epoch visibly unmeasured rather than assumed zero. These are consequential side effects not derivable from readOnly=false/destructive=false.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and epoch consequence are front-loaded and almost every sentence carries information. A few asides ('which is the one moment anybody actually knows it') add flavor at some cost to terseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For an 11-parameter write tool with nested details and an output schema, the description covers the epoch mechanics, offset omission state, and evidence capture well. It does not address duplicate or repeated logging behavior, which matters given idempotentHint=false.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, but the description still adds meaning: offset_value is defined as a signed clicks-to-add correction along the collar's direction, offset_source enumerates pull/counted/typed, and details is tied to the per-task details_fields published elsewhere.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource: 'Log a cleaning or maintenance job against a piece of equipment.' It also distinguishes itself from read-side siblings by pointing at list_grinders/list_machines for due state and from set_maintenance_offset by owning the offset recorded at log time.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear conditions: record the offset only if you counted clicks at reassembly; any vocabulary task may be logged even if the maker advises against it; read due state from list_grinders/list_machines. No explicit when-not guidance relative to list_maintenance_due or set_maintenance_offset, which leaves some sibling ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

log_recommendationLog a recommendationCInspect

Log a recommended dialing change to allow contradiction/oscillation checking (G2).

NameRequiredDescriptionDefault
leverYes
directionYes
rationaleYes
confidenceYes
cited_rulesNo
based_on_shotNo

Output Schema

NameRequiredDescription
statusYes
messageYes

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already carry the mutation and non-idempotency signals, so the description's job is lighter. It adds context that this records a recommendation rather than a definitive change and notes the G2 analysis purpose, but says nothing about repeated logging effects or return behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is one tight sentence with no filler, and the key action is front-loaded. The parenthetical G2 is opaque but does not add bloat.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a six-parameter write tool with four required fields and no schema descriptions, this single sentence is too sparse. The purpose is clear, but the agent still lacks guidance on how to fill parameters, especially cited_rules and based_on_shot, and when this tool should be preferred over siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description only hints at a 'dialing change,' which loosely covers lever and direction. It does not explain confidence, cited_rules, or based_on_shot, and does not compensate for the schema's lack of parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb—log—and a distinct object, a recommended dialing change, which separates it from sibling log tools like log_shot and log_maintenance. It does not explicitly name an alternative, so it stops short of full differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a purpose ('to allow contradiction/oscillation checking') but no explicit when-to-use or when-not-to-use guidance. It does not point to any alternative tools or exclusions, leaving the agent to infer when logging is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

log_shotLog a shotAInspect

Log an espresso or alternative shot/brew attempt. Uses the active context (bean/grinder/machine/program) unless overridden via bean_id/grinder_id/machine_id/program_id. ALWAYS pass bean_id explicitly when the user names a specific coffee, so the shot cannot land on the wrong bag. Backdate with pulled_at when the shot happened earlier. When the user reports taste (bitter, sour, harsh, hollow…), include matching sensory_tags: the diagnosis engine reads tags and metrics, not free-text flavor_notes. When the user tasted the shot and reports it as GOOD/clean (no defects), pass tasted=1 with no sensory_tags. Otherwise a tag-less, rating-less shot is filed taste-pending as if never tasted.

NameRequiredDescriptionDefault
tdsNoTotal Dissolved Solids percentage (e.g. 9.1 or 1.35)
bag_idNoWhich bag of the coffee the shot came from; defaults to its current (open) bag. Bag numbers are on list_beans' bags[]
dose_gYes
ratingNo
tastedNo1 = the shot was tasted at log time. The explicit way to record a CLEAN tasting: sensory tags all describe defects, so a good shot has none. Without this flag (or a rating) it would be filed taste-pending. Tags or a rating also mark a shot as tasted; 0 (default) = taste later.
time_sYes
bean_idNoOverride: log against this bean instead of the active one (null = active)
verdictNo
yield_gYes
water_idNoOptional references to waters formulation
best_brewNo1 if marked as best brew, 0 otherwise
favouriteNo1 if favourite, 0 otherwise
pulled_atNoWhen the shot was actually pulled (ISO 8601 or "YYYY-MM-DD HH:MM:SS"), for backdated entries. Defaults to now (null = now). When backdating, pass the returned shot_id to diagnose_shot explicitly, because the default diagnosis target is the chronologically latest shot.
beverage_gNoYield mass excluding cup/vessel weight in grams
grinder_idNoOverride: grinder used, if not the active one (null = active)
machine_idNoOverride: machine used, if not the active one (null = active)
program_idNoOverride: machine program used, if not the active one (null = active)
temp_tasteNo
vessel_nameNoCup/vessel name
bloom_time_sNoPre-wetting or bloom duration in seconds
drink_intentNo
flavor_notesNo
flow_profileNoOpaque flow profile data (JSON/TEXT)
method_toolsNoJSON representation of method tools used (e.g., paper filters, screen)
observationsNo
sensory_tagsNo
finish_actionYes
temperature_cNoBrew temperature in Celsius
data_confidenceNomeasured = weighed/timed live; recalled = from memory; estimated = a best guess (e.g. "~25s" after missing the timer)
vessel_weight_gNoCup/vessel weight in grams
machine_settingsNoOnly when this one shot was pulled with a selector somewhere other than the machine's recorded settings: {key: position}. The shot is otherwise stamped with those automatically; a lasting change goes through update_machine.
pressure_profileNoPressure profile description or values
first_drip_time_sNoTime to first drip in seconds

Output Schema

NameRequiredDescription
statusYes
messageYes
shot_idYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (write, non-idempotent, non-destructive) it discloses two non-obvious behaviors: shots with no tags/rating are filed as taste-pending, and the diagnosis engine reads sensory_tags rather than flavor_notes. It does not mention the backdating interaction with diagnose_shot's default target (that hint lives in the schema), so a small gap remains.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and active-context semantics, then ordered by decision importance (bean override → backdating → tasting). Dense but nearly every clause carries an actionable rule; the closing sentence is long and could be split, but nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 33-parameter mutation tool with an output schema, the description covers the risky/ambiguous decisions well, but the four required fields (dose_g, yield_g, time_s, finish_action) have no description in either schema or text, and the many optional fields get no framing. Adequate but not complete for this surface area.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%, so the description must add meaning for high-stakes params – it does for bean_id (wrong-bag risk), pulled_at (backdating semantics) and the sensory_tags/tasted/flavor_notes relationship (tags are defect-oriented, so a clean shot has none). Many optional params (verdict, drink_intent, observations, the metric fields) are left to the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

"Log an espresso or alternative shot/brew attempt" states a specific verb and resource, and the body makes the scope concrete (active-context stamping, overrides, tasting state). It is clearly distinguishable from siblings like update_shot (edits an existing shot), diagnose_shot (analysis) and log_recommendation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit conditional rules are given for the three decisions that matter: pass bean_id when the user names a coffee, use pulled_at for backdating, and pass tasted=1 with no tags for a clean shot. It also names the alternative handling (rating or tags) and the consequence of doing neither.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

merge_beansMerge two coffeesA
Destructive
Inspect

Fold one coffee into another when the same coffee was registered twice. Every bag, shot, recipe and recommendation of from_bean_id moves to into_bean_id; the active coffee follows if it was the one merged away; origins move only if the target has none. Where both hold a locked recipe for the same grinder, machine, program and drink, the newer lock wins and the older is deleted. The source coffee is archived, never deleted. Irreversible: confirm both numbers with the user first (list_beans shows them).

NameRequiredDescriptionDefault
reasonNoOptional note, recorded on the surviving coffee's notes with the merge
from_bean_idYesThe duplicate: the coffee number that will be emptied and archived
into_bean_idYesThe coffee number that keeps everything

Output Schema

NameRequiredDescription
movedYes
statusYes
messageYes
into_bean_idYes
active_followedYes
replaced_recipesYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far exceeds what the annotations convey: annotations only flag destructive=true and idempotent=false, while the description explains exactly what moves (bags, shots, recipes, recommendations), the conditional behaviors (origins only if target has none, newer lock wins and older is deleted), that the source is archived not deleted, and that the operation is irreversible.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Leads with the trigger, then the consequences, then the safety warning — all within four sentences with no filler. The dense lock-conflict clause is the only heavy sentence, but it is information the agent cannot get elsewhere.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

A destructive mutation with an output schema needs the description to cover data migration, edge cases and irreversibility; it does all three. Return values are covered by the output schema, so nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already labels from_bean_id as the duplicate to be archived and into_bean_id as the survivor, so the description is not the sole source. It still adds directional reinforcement and points the agent at list_beans to resolve the numbers before calling.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('fold one coffee into another') plus the exact condition that motivates it ('when the same coffee was registered twice'). This clearly distinguishes merge_beans from update_bean and the register_* siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger condition (duplicate registration) and a required pre-flight step: confirm both numbers with the user and look them up via list_beans. It does not name the non-duplicate alternative (update_bean) for cases where the coffees are genuinely distinct, so the when-not case is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_bagRegister a bagAInspect

Register another bag of a coffee that is already in list_beans: the same roaster's product, bought again. It keeps the coffee's recipes, notes and history; only the purchase facts (roast date, weight, price, state) are new. Does not change the active coffee. Returns a bag_id: bag numbers are their own series, separate from bean numbers. For a coffee that is not in list_beans yet, call register_coffee instead.

NameRequiredDescriptionDefault
eanNo
costNo
stateNoDefaults to sealed; the first log_shot against it opens it
bean_idYesThis account's own coffee number, from list_beans. Never guess one.
qr_codeNo
storageNo
buy_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
currencyNo
bag_notesNoNotes about THIS bag (a batch note, where it was bought). The coffee's own description lives on the coffee, in notes.
product_idNoSet by the apps. Leave out.
roast_dateYesCalendar day in YYYY-MM-DD format: a day, not a timestamp
opened_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
bag_weight_gNoBag weight in grams
best_by_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
lot_revision_idNoSet by the apps. Leave out.
catalog_coffee_idNoSet by the apps. Leave out.

Output Schema

NameRequiredDescription
bag_idYes
statusYes
bean_idYes
messageYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare a non-readonly, non-idempotent, non-destructive create, and the description adds real behavioral context beyond that: what is inherited (recipes, notes, history) versus what is new (roast date, weight, price, state), plus the bag_id return and that bag numbers are a separate series from bean numbers. It stops short of covering failure modes or duplicate handling, but the inheritance/no-active-change details are the valuable part.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the core action before the inheritance rule, the return-value caveat, and the alternative. Nothing repeats the name or the schema, and every sentence carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter create tool with annotations and an output schema, the description covers the essential gap areas: which entity is being extended, what is conserved, the id semantics of the return, and the sibling to use instead. Remaining detail (formats, defaults, optional fields) is delegated correctly to the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 69% and the schema already documents the dates, state default, bag_notes scoping and product_id guidance, so the baseline is a 3. The description does add a useful partition by naming the 'purchase facts (roast date, weight, price, state)' that are new per bag, but it leaves the remaining undocumented fields (ean, cost, storage, currency) untouched.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Register another bag of a coffee that is already in list_beans') and pins the scope to a repeat purchase of the same roaster's product. It explicitly distinguishes itself from register_coffee, so an agent can choose without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives both the when ('coffee already in list_beans... bought again') and the when-not with a named alternative ('For a coffee that is not in list_beans yet, call register_coffee instead'). It also rules out a side effect an agent might assume ('Does not change the active coffee').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_coffeeRegister a coffeeAInspect

Register a coffee that is NOT yet in list_beans, together with its first bag. It does not switch a coffee in use: it becomes active only when no coffee is in use (none active, or the active one archived or with every bag finished); otherwise call set_active with its bean_id when the user starts brewing it. The active field of the reply says which. The coffee is the roaster's product (roaster, origins, variety, process, roast level, roasting type, cupping score, aromatics, roaster notes, rest window); the bag is this purchase (roast date, weight, price, state). For another bag of a coffee already on file call register_bag instead: it keeps the recipes and notes. Returns bean_id and the first bag's bag_id.

NameRequiredDescriptionDefault
eanNo
urlNo
bodyNoFree text; roasters usually say low, medium or high
costNo
nameYes
notesNoThe coffee's own description: the roaster's tasting notes, the dialing approach that worked. Per bag notes go in bag_notes.
stateNo
originNo
ratingNo
acidityNoFree text; roasters usually say low, medium or high
co2e_kgNo
originsNoList of bean origin details
qr_codeNo
roasterNo
storageNo
varietyNo
archivedNo1 if archived, 0 otherwise
bean_mixNoE.g. blend details
buy_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
currencyNo
finishedNo1 if finished, 0 otherwise
aromaticsNo
bag_notesNo
favouriteNo1 if favourite, 0 otherwise
frozen_atNoCalendar day the bag went into the freezer, YYYY-MM-DD: a day, not a timestamp
sweetnessNoFree text; roasters usually say low, medium or high
product_idNoSet by the apps. Leave out.
roast_dateYesCalendar day in YYYY-MM-DD format: a day, not a timestamp
arabica_pctNoPercent arabica, 100 for a pure arabica; the rest robusta
attachmentsNoJSON or comma-separated list of attachments
frozen_noteNo
opened_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
roast_levelYes
roast_rangeNo
unfrozen_atNoCalendar day the bag came out, YYYY-MM-DD: a day, not a timestamp
bag_weight_gNoBag weight in grams
best_by_dateNoCalendar day in YYYY-MM-DD format: a day, not a timestamp
process_typeYes
roast_customNo
decaffeinatedNo1 if decaf, 0 if regular
dial_categoryNoDialing behavior class: classic = medium/traditional espresso roasts (chocolate/nut, balanced); dark = genuinely dark/roasty; the light categories cover Nordic and ultra-light styles.
roasting_typeNo
cupping_pointsNo
frozen_storageNo
lot_revision_idNoSet by the apps. Leave out.
catalog_coffee_idNoSet by the apps. Leave out.
rest_window_days_maxNo
rest_window_days_minNo

Output Schema

NameRequiredDescription
activeYes
bag_idYes
statusYes
bean_idYes
messageYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations by disclosing the activation side effect chain: the new coffee does not become active unless no coffee is in use (none active, active archived, or all bags finished), and the reply's `active` field reports which happened. Annotations only say non-readonly, non-idempotent, non-destructive; the active-state logic is the non-obvious behaviour an agent must know.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the decisive constraint (NOT yet in list_beans) and the alternative routing. The parenthetical field enumerations are long but justified because they are what separates bean from bag fields; the text is dense with little filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 48-parameter creation tool, it covers the essentials an agent needs before calling: uniqueness requirement, the bean/bag split, the activation consequence, and the returned ids (bean_id, bag_id) even though an output schema exists. It leaves unclear how duplicates are detected (e.g. via ean) and what validation failures look like, which is a modest gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Adds real meaning over the 48%-covered schema by partitioning the 48 parameters into coffee-level fields (roaster, origins, variety, process, roast level, roasting type, cupping score, aromatics, notes, rest window) versus bag-level fields (roast date, weight, price, state), which the schema does not do. Many parameters (ean, url, qr_code, currency, storage, attachments) remain unmentioned, so it stops short of full compensation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource plus a hard precondition: register a coffee NOT yet in list_beans, together with its first bag. It also names the sibling it is not (register_bag) and the neighbour it does not replace (set_active), so an agent can distinguish it from siblings without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use ('NOT yet in list_beans'), when-not ('For another bag of a coffee already on file call register_bag instead'), and what to do afterward ('call set_active with its bean_id when the user starts brewing it'). Exclusions and alternatives are spelled out rather than implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_grinderRegister a grinderAInspect

Register a manual or electric grinder with its dial settings scheme. It becomes the active grinder only when no grinder is in use (none active, or the active one archived); otherwise call set_active with its grinder_id to switch to it. The active field of the reply says which.

NameRequiredDescriptionDefault
nameYesThe name/model of the grinder, e.g. Kinu M47
notesNoAdditional notes
photoNoPhoto path or URL
archivedNo1 if archived, 0 if active
burr_typeYes
max_valueNoOptional travel max bounds
min_valueNoOptional travel min bounds
catalog_idNoOptional: the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. "none" says it is not in the catalog.
motor_typeYes
nominal_stepYesSmallest adjustment increment (e.g. 1 click or 0.1collar units)
setting_schemeYesCollar mark style: single clicks or compound (rotation.number.clicks)
components_specNoJSON mapping string representing compound adjustment math
microns_per_stepNoOptional mechanical burr travel microns per nominal step
direction_conventionYes

Output Schema

NameRequiredDescription
activeYes
statusYes
messageYes
grinder_idYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare non-readOnly, non-idempotent, non-destructive, but the description adds the crucial side effect the annotations cannot express: activation is conditional on no active/archived grinder, and the reply's `active` field tells which. That is genuine behavioral disclosure beyond structured data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with the core action front-loaded and the conditional-activation rule immediately after. No filler, though the final sentence about the `active` field is slightly compressed in wording.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be detailed, yet the description still points to the `active` reply field, which is the one output an agent must act on. Combined with the activation rule, an agent has enough to call it correctly; only the post-creation edit path is offloaded to inference.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 79%, so the schema already documents nearly all 14 parameters including the setting_scheme and catalog_id semantics. The description adds only the aggregate concept of a 'dial settings scheme' and does not clarify any individual parameter beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Register a manual or electric grinder with its dial settings scheme'), which immediately separates it from update_grinder and set_active in the sibling list. The scope (creating a new grinder record vs. mutating state) is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the alternative for switching: 'otherwise call set_active with its grinder_id to switch to it,' and the condition under which this tool activates the grinder. It does not mention update_grinder for later edits, so it stops one step short of full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_machineRegister an espresso machineAInspect

Register an espresso or alternative preparation machine. It becomes the active machine only when no machine is in use (none active, or the active one archived); otherwise call set_active with its machine_id to switch to it. The active field of the reply says which.

NameRequiredDescriptionDefault
nameYesMachine name/model, e.g. Dedica EC685
notesNo
photoNoPhoto path or URL
toolsNoJSON or comma-separated tools, e.g., paper filters, flow control, metal mesh
archivedNo1 if archived, 0 if active
prep_typeNoPreparation equipment type. One of: espresso_machine, lever_press, moka_pot, aeropress, french_press, v60_dripper, cold_brew. Lower snake_case.
catalog_idNoOptional: the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. "none" says it is not in the catalog.
prep_styleNoPreparation style. One of: espresso, filter, immersion, percolation. Only "espresso" is diagnosed by the dialing engine (G8). Anything else is recorded but never dialed, so do not guess this field.
boiler_typeNo
hardness_dhNoOptional: the TOTAL hardness (GH) of the water this machine runs on, in °dH (German degrees; ppm as CaCO3 ÷ 17.85), as a test strip reports it. Picks the band of a maker's hardness table for descaling and filter life
control_typeYes
connected_deviceNoMetadata mapping to smart hardware APIs
basket_size_gramsNo
built_in_grinder_idNoid of a grinder built into this machine (bean-to-cup / integrated grinder)

Output Schema

NameRequiredDescription
activeYes
statusYes
messageYes
machine_idYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare a non-read-only, non-destructive, non-idempotent write, but say nothing about the side effect of activation state; the description adds that meaningful behavior and points at the reply's `active` field to confirm it. It does not cover duplicate-name handling or validation failures, which is the remaining gap for a creation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with zero filler; the activation condition, which is the only non-obvious consequence of the call, is front-loaded right after the purpose statement.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 14-parameter, 2-required creation tool with an output schema, the description covers the one behavior an agent cannot infer from structured data: the conditional activation. Return values are covered by the output schema, so the remaining uncovered params (notes, boiler_type, basket_size_grams) are a minor omission rather than a blocking one.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 71% and is unusually rich (catalog_id, hardness_dh, prep_style all carry their own semantics and warnings), so the baseline is a 3. The description itself adds no parameter-level meaning beyond what the schema already states, relying entirely on the structured fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb (register) and resource (espresso or alternative preparation machine), and immediately scopes it against the neighbouring alternatives by naming set_active for the switch case. An agent can distinguish it from update_machine and register_grinder without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States the exact condition under which the new machine becomes active (none active, or the active one archived) and the explicit alternative when that condition fails: call set_active with its machine_id. This is the when/when-not/alternative triad in two sentences.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_programRegister a machine programA
Destructive
Inspect

Register a programmed shot button on a machine. A program cuts the shot off either by weight (program_type "volumetric", set target_volume_g) or by time (program_type "timed", set target_time_s). One machine can have a mix of both. A program_number the machine already has is overwritten with these settings; shots logged on it keep pointing at it.

NameRequiredDescriptionDefault
machine_idYes
descriptionNo
program_typeNoCutoff method: "volumetric" (weight) or "timed" (duration). Defaults to volumetric.
target_time_sNoTarget shot duration cutoff in seconds (required for timed programs)
volume_sourceNoConfidence in the stored cutoff value, for either type
program_numberYesProgram number / button index (e.g. 1 or 2)
target_volume_gNoTarget yield cutoff in grams (required for volumetric programs)

Output Schema

NameRequiredDescription
statusYes
messageYes
program_idYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and openWorldHint=false, so the mutation profile is known. The description usefully adds what actually gets destroyed: an existing program_number is overwritten while previously logged shots keep pointing at it, which is valuable context beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the core action and then the type semantics and overwrite consequence. Every sentence carries information, though it could be slightly terser.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema exists so return values need not be explained, and the description covers the two program types and overwrite behavior well. The omission of volume_source semantics and any error/permission notes keeps it from being fully complete for a 7-parameter mutation tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 71%, and the description reinforces the key pairing (program_type "volumetric" → target_volume_g, "timed" → target_time_s), adding the relational logic the schema states per-field. However it says nothing about volume_source, description, or machine_id, so it only partially compensates for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Register a programmed shot button on a machine") and immediately defines what a program is and how it terminates a shot. This clearly distinguishes it from sibling registration tools like register_machine or register_grinder, which target different resources.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description distinguishes the two program modes (volumetric vs timed) and the fields each requires, which implies usage, but it never says when to call this versus update_machine or register_machine, nor any prerequisites. Usage is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_scaleRegister a scaleCInspect

Register a brewing scale: the instrument that weighs the dose and the yield.

NameRequiredDescriptionDefault
nameYesScale name/model, e.g. Acaia Lunar, Timemore Black Mirror Basic 2
notesNoAdditional notes
powerNoHow it is powered or recharged
depth_mmNoPlatform depth in mm
width_mmNoPlatform width in mm. Measure your drip tray
auto_tareNo1 if it zeroes itself when a vessel is set down, 0 if not, null if unstated
height_mmNoOverall height in mm: the dimension that decides whether it fits under a group head
auto_timerNo1 if the timer starts on first flow, 0 if not, null if unstated
capacity_gNoMaximum weight in grams. The cup or portafilter counts against it
scale_typeYesWhat the scale is built for: espresso (drip-tray footprint), brew (pour-over platform), dual (both modes), connected (app/controller integration), dosing (gravimetric dispensing), training, bench (high capacity, coarse)
connectivityNoHow the scale talks to an app or a machine controller
resolution_gNoFinest published readability in grams. Leave null unless the maker states it
water_resistanceNoIEC 60529 ingress code as published, e.g. IPX4, IPX6, IP67, or none
flow_rate_displayNo1 if the scale shows live flow rate in g/s, 0 if it does not, null if the maker never said
resolution_bandedNo1 when resolution_g only holds in the lowest weight band and coarsens with load. Put the ladder in notes

Output Schema

NameRequiredDescription
statusYes
messageYes
scale_idYes

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=false, idempotentHint=false and destructiveHint=false, so the description only needs to add context beyond that, and it adds almost none. It does not say what happens on a duplicate name, whether the call fails or merges, or what the response contains. The phrase about weighing dose and yield is a definition, not behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no waste, but it is undersized for a 15-parameter creation tool; the brevity is under-specification rather than economy. Nothing in the sentence is wasted, yet it does not use its position to route the agent anywhere.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 15-param create tool with a rich sibling set, the description omits duplicate handling, the update_scale alternative, and any note about which fields matter most (despite the schema hinting at fit under a group head). An output schema and annotations exist, so return values need not be explained, but the calling context is thin.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the 15 parameters are richly documented inline, including enum meanings and null conventions, so the schema does the heavy lifting. The description adds no parameter meaning at all. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb and resource (register a scale) and glosses the resource as the weighing instrument for dose and yield, which distinguishes it from register_grinder/register_machine. It does not explicitly separate it from update_scale, but the create-vs-update distinction is carried by the verb 'register'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance at all: no mention of using update_scale for an existing scale, no pointer to list_scales to check for duplicates, and no prerequisites. The only signal is the implicit 'register = new record' reading of the verb.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

register_waterRegister a water recipeBInspect

Register a custom or commercial water formulation.

NameRequiredDescriptionDefault
ghNoGeneral hardness (GH) in ppm
khNoKarbonate hardness (KH) in ppm
tdsNoTDS in ppm
nameYesWater formulation name, e.g. Lotus Light & Bright, Third Wave Water
typeNoType, e.g., mineralized, tap, distilled, RO
notesNoAdditional notes
sodiumNoSodium concentration in ppm
calciumNoCalcium concentration in ppm
magnesiumNoMagnesium concentration in ppm

Output Schema

NameRequiredDescription
statusYes
messageYes
water_idYes

TDQS

B3.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already signal that this is not a read-only operation, but the description adds no behavioral detail beyond that, such as whether duplicate names overwrite, whether registration requires approval, or any side effects. It does not contradict the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence with no filler, front-loading the core action and resource. It is appropriately sized for the simplicity of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a registration tool, the description combined with full schema coverage, annotations, and an output schema provides enough context for an agent to select and invoke the tool. It is slightly incomplete only because it omits usage guidance relative to sibling tools, but that is covered under usage_guidelines.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% property description coverage, so the schema carries the parameter documentation burden. The description adds no additional parameter meaning beyond what is already in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'Register' with the resource 'water formulation', which matches the title and clearly identifies the tool's purpose. It also distinguishes this tool from sibling register_* tools by naming water as the entity, and custom/commercial scope adds useful clarity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance on when to use this tool over alternatives such as list_waters or update_* tools. No exclusions, prerequisites, or comparison with sibling tools are provided; usage context is only implied by the name and title.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_activeSet active equipmentA
Idempotent
Inspect

Switch the active bean, grinder or machine for this account. The numbers come from list_beans, list_grinders and list_machines. Registering a coffee, grinder or machine makes it active only when nothing of that kind is in use (the active field of the reply says which), so call this to start using something new as well as to switch back.

NameRequiredDescriptionDefault
bean_idNoThis account's own bean number, as returned by list_beans. Never guess one; null = leave unchanged
grinder_idNoThis account's own grinder number, as returned by list_grinders. Never guess one; null = leave unchanged
machine_idNoThis account's own machine number, as returned by list_machines. Never guess one; null = leave unchanged
program_idNoA program number from the active machine's list; switching machines clears it. null = leave unchanged

Output Schema

NameRequiredDescription
statusYes
messageYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare idempotentHint=true and destructiveHint=false, so the safety profile is partly covered. The description adds useful behavioral context beyond them: partial updates leave fields unchanged (matching the null parameters) and the reply's `active` field reports what actually got activated, which is genuine non-obvious behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the core action, then provenance, then the non-obvious activation caveat. Dense and mostly waste-free, though the final sentence is slightly long for what it conveys.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return-value docs aren't needed, and the description still hints at the reply's `active` field. Combined with full schema coverage and annotations, an agent has what it needs; only the absence of explicit alternative routing keeps it from a 5.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and each parameter already documents 'null = leave unchanged' and 'Never guess one'. The description only adds the provenance of the numbers (list_beans/list_grinders/list_machines), which largely duplicates the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('switch the active') and resource (bean, grinder, machine) for this account, and explicitly sets it apart from the register_* siblings by noting that registration only activates when nothing of that kind is in use. An agent can tell this is the explicit activation setter, not a create/update tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear context for when to call it: to start using something new (after registering) as well as to switch back to an existing item. It also points to the list_* tools that supply valid numbers, but doesn't enumerate alternatives such as update_bean or set_grinder_position for other forms of change.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_equipment_archivedArchive or restore equipmentA
Idempotent
Inspect

Archive or restore a grinder, machine, water, or scale.

NameRequiredDescriptionDefault
idYesThe ID of the equipment
kindYesThe kind of equipment to archive/restore
archivedYes1 to archive, 0 to restore

Output Schema

NameRequiredDescription
statusYes
messageYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate a non-read-only, non-destructive, idempotent operation. The description adds the useful behavioral nuance that the operation can both archive and restore, implying reversibility, but it does not disclose side effects, permissions, or what happens to equipment once archived.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence: action first, then the complete resource scope. Every word carries information, with no redundant filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple three-parameter tool with fully documented schema fields, enum constraints, safety annotations, and an output schema, the description provides sufficient context for correct invocation. Remaining details such as return format are already handled elsewhere.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and each parameter already has a clear description, including enum values for kind and archived. The description only restates the equipment kinds from the schema and adds no new parameter-level meaning.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses precise verbs ('Archive or restore') and explicitly names all supported resource kinds ('grinder, machine, water, or scale'), matching the kind enum exactly. This makes the tool's purpose immediately distinguishable from siblings like set_active or register_*.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear usage context: use this tool when you need to archive or restore one of the listed equipment types. It does not name alternatives or explicit when-not scenarios, but the action is unique among the sibling tools, so no routing conflict exists.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_grinder_positionSet grinder positionA
DestructiveIdempotent
Inspect

Set the current grinder collar position. source="measured" resets verification freshness. It replaces the stored position; no history of earlier positions is kept.

NameRequiredDescriptionDefault
sourceYesWhether setting is verified ("measured") or guess ("recalled"/"assumed")
grinder_idYes
setting_labelYesVerbatim display label, e.g. "1.1.2"

Output Schema

NameRequiredDescription
sourceYes
statusYes
messageYes
grinder_idYes
setting_labelYes
position_valueYes

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true; the description adds real value by explaining what 'destructive' means here — the stored position is replaced and no history is retained — plus the side effect of a measured source on verification freshness. It stops short of describing permissions or the response shape.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the core action, then the consequence of the source value, then the overwrite behavior. No filler, every sentence carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no explanation, and the description covers the destructive overwrite semantics and the key source side effect. It is nearly complete for a 3-parameter mutation tool, with only minor gaps around permissions and freshness semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%, with source and setting_label documented in the schema. The description adds meaning to source="measured" by tying it to verification freshness, which the schema does not state, but it says nothing about grinder_id and only partially compensates for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource — setting the grinder collar position — which separates it from the generic update_grinder sibling. It does not explicitly name or contrast with alternatives, but the scope is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit 'use when / don't use when' guidance or named alternative, but the note that source="measured" resets verification freshness implies when a verified entry is warranted versus a guess. Usage is inferable rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_maintenance_offsetSet calibration offsetA
DestructiveIdempotent
Inspect

Record how far a maintenance job moved the reference, on an entry that opened a calibration epoch. Takes an ENTRY, not a piece of equipment: the offset belongs to the event that caused it, which is what lets several of them compose for a recipe locked before all of them. Zero is a legitimate measurement and means "measured, and it had not moved". Omit offset_value entirely to clear it back to unmeasured.

NameRequiredDescriptionDefault
sourceNoHow it was arrived at: pull, counted, or typed
entry_idYesThe maintenance entry that opened the epoch, from list_grinders
evidenceNoOne line recording what it rested on, e.g. "Reference pull 1.38 g/s against 1.74 g/s locked."
offset_valueNoClicks to ADD to a pre-clean reading to get the same grind today, signed along the collar's own number direction. 0 records "unchanged"; omit to clear the measurement

Output Schema

NameRequiredDescription
entryYes
messageYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

It provides significant behavioral detail beyond the structured fields by defining the logical meaning of 0 (unchanged), the semantic implication of omitting offset_value (clearing measurement), and the relational context (offsets composing for a locked recipe). These details add substantial clarity.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The prose is highly technical yet perfectly structured. Every sentence contributes vital, specific information (e.g., 'Zero is a legitimate measurement,' 'takes an ENTRY') and the flow is logical and dense.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description provides a comprehensive context for a highly technical tool. By explaining concepts like 'calibration epoch' and the dependency on the entry ID, it ensures the AI agent has the full necessary context for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the baseline is 3. The description elevates this by explaining the 'signed direction' of the offset and the context of the 'entry,' adding crucial meaning that transcends the formal type definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description is highly specific, stating that it records how a maintenance job moved the reference and attaches this data to the entry that opened a calibration epoch. This clearly defines the tool's domain within the overall system architecture.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

While it clarifies that the offset belongs to an 'ENTRY, not a piece of equipment,' which provides crucial scope guidance, it does not explicitly suggest alternatives like 'log_maintenance,' making it an implied usage scenario rather than a direct comparison.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_maintenance_scheduleSet a maintenance scheduleA
DestructiveIdempotent
Inspect

Set the user's own schedule for one maintenance task on one grinder or machine: their own interval, switching it off, or muting the reminder until a date. Only the keys you send in fields change; send every: null to go back to the maker's guidance and muted_until: null to unmute. Use it when the user states how often they do a task or asks to be left alone about one, never to invent an interval. The task's class, interval and due state are read back from list_grinders and list_machines.

NameRequiredDescriptionDefault
taskYesThe task, as its value in that equipment's maintenance.tasks
fieldsNo
changed_atNoOptional: when the user made this change (ISO 8601, UTC). An app sending a queued edit passes it, so an older edit never overwrites a newer one; it is then answered with applied: false
equipment_idYesThis account's own grinder or machine number, from list_grinders or list_machines
equipment_kindYesWhich kind of equipment the task is on

Output Schema

NameRequiredDescription
dueYes
taskYes
classYes
everyYes
appliedYes
messageYes
scheduleYes
equipment_idYes
every_sourceYes
equipment_kindYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite annotations already declaring mutation and idempotency, the description adds substantial behavioral context: partial-update semantics ('Only the keys you send in fields change'), null-as-reset behavior for both every and muted_until, what enabled:false does without forgetting the interval, and how changed_at resolves queued-edit conflicts with applied:false. This is exactly the kind of detail annotations cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the core action and then the partial-update rule and usage trigger. Dense but nearly every clause carries operational meaning; the final read-back sentence is slightly tangential but still useful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex nested-parameter mutation with an output schema and annotations, the description covers when to use it, how partial updates behave, reset semantics, and where to read task state. Nothing an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 80%, and the nested schema already documents the null-reset semantics for every and muted_until, enabled:false, and changed_at conflict handling. The description largely restates those same semantics rather than adding new meaning, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb and resource (set the user's own schedule for one maintenance task on one grinder/machine) and enumerates the three modes: own interval, switch off, or mute. It is clearly distinguished from siblings like log_maintenance and set_maintenance_offset, which handle logging and offsets rather than a full schedule.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger ('when the user states how often they do a task or asks to be left alone about one') and an explicit prohibition ('never to invent an interval'). It also points to list_grinders and list_machines for reading back state, but does not explicitly contrast with set_maintenance_offset, so it falls just short of naming alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

suggest_next_stepSuggest the next stepB
Read-onlyIdempotent
Inspect

Retrieve the single next experiment step from the reasoning engine.

NameRequiredDescriptionDefault
bean_idYes

Output Schema

NameRequiredDescription
categoryYes
warningsYes
next_stepYes
warning_codesYes

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds only that exactly one step is returned; it says nothing about what happens when no step is available, whether state must exist first, or how fresh the suggestion is.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler. It is efficient, though the terseness contributes to the missing context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be described. However, for a tool whose whole purpose is ambiguity-prone ('next step'), the bean_id semantics and the precondition for getting a suggestion are missing, leaving the definition only barely sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

One required parameter with 0% schema description coverage, and the description never explains what bean_id identifies or that it comes from list_beans/register_bean. The description does nothing to compensate for the undocumented parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb ('Retrieve') and resource ('the single next experiment step from the reasoning engine'), which is enough to distinguish it from the CRUD/registration siblings. What an 'experiment step' actually is in this coffee-tooling domain is left implicit, so it stops short of full clarity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to call this versus diagnose_shot, get_dial_state, get_rule or the other read-oriented siblings. An agent must guess that this is the recommendation/advice endpoint rather than a lookup.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_bagUpdate a bagA
Destructive
Inspect

Change bag-level facts on one bag of a coffee: open, seal, freeze, thaw or finish it, correct its roast date, weight, grams left or price. Bag numbers come from the bags[] on list_beans (or current_bag_id). For the coffee itself (roaster, origin, roast level, rest window, notes), use update_bean.

NameRequiredDescriptionDefault
bag_idYesThis account's own bag number, from list_beans' bags[]. Never guess one.
fieldsYes
reasonNoOptional free-text note, appended to this bag's notes as an ordinary line. Omit it and nothing is written.

Output Schema

NameRequiredDescription
statusYes
messageYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=false, so the safety profile is covered elsewhere. The description does convey that this mutates bag status (open/seal/freeze/thaw/finish), which is useful context, but it says nothing about whether omitted fields are preserved, whether changes are reversible, or what the response confirms.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences plus a single routing sentence, with the primary purpose front-loaded and the sibling disambiguation last. Every sentence carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Output schema and annotations carry the return shape and safety profile, and the description covers purpose, identifier sourcing, and sibling routing. A mutation tool with a 19-field nested object would ideally state whether the fields object is a partial patch (unmentioned fields preserved) and confirm the destructive semantics, which is the main missing piece.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67% and rich field-level descriptions already explain grams_remaining and weight_adjustment_g. The description adds the mapping from plain-language fields (roast date, weight, grams left, price) to the fields object, but says nothing about ean, qr_code, storage, bag_notes, or the optional reason parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (change), resource (bag-level facts on one bag of a coffee), and enumerates the concrete operations (open, seal, freeze, thaw, finish, correct roast date/weight/grams left/price). It explicitly distinguishes itself from the sibling update_bean, which handles coffee-level attributes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tells the agent where bag identifiers come from (bags[] on list_beans or current_bag_id) and names the alternative tool (update_bean) for coffee-level edits. The only minor gap is that it does not spell out that state transitions happen by setting state = open/frozen/etc., but the operation verbs imply it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_beanUpdate a coffeeA
Destructive
Inspect

Update a coffee's own fields (roaster, origins, roast level, rest window, notes, rating…). Bag-shaped fields sent here (state, roast_date, bag_weight_g, grams_remaining, cost…) land on the coffee's CURRENT bag. Use update_bag when it has more than one. Changing dial_category changes what the engine advises (G5, C10), so say why in the optional note.

NameRequiredDescriptionDefault
fieldsYes
reasonNoOptional free-text note, appended as an ordinary line to the coffee's notes, or to the current bag's bag_notes when the call changed only bag fields. Omit it and nothing is written.
bean_idYes

Output Schema

NameRequiredDescription
statusYes
messageYes

TDQS

A4.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare this a non-idempotent, destructive, closed-world mutation, so the safety profile is covered. The description still earns credit for disclosing a non-obvious cross-resource side effect: bag-shaped fields passed here mutate the coffee's CURRENT bag rather than the coffee. It does not explain partial-update semantics (whether omitted fields are preserved), merge behavior, or failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with scope, then the routing rule, then the caution — nothing redundant. The '(G5, C10)' code references are opaque to an outside reader and cost a little clarity, and the parenthetical field lists are dense.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need no explanation, and the annotations cover the destructive/non-idempotent profile. What remains thin is update semantics: whether this is a partial patch, how nested `origins` arrays are treated, and what happens when both coffee and bag fields are sent together.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 33%, and the flat `fields` bag mixes coffee-level and bag-level keys with no structural distinction in the schema itself. The description supplies exactly that missing semantics by naming which fields land on the bag versus the coffee, and it explains the purpose of the `reason` note. It falls short of fully compensating because the field lists are truncated ('…') and replace-vs-append semantics for individual fields are only partially addressed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Update a coffee's own fields') and enumerates representative fields so the scope is concrete. It immediately contrasts with the sibling update_bag, so an agent can pick between the two without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: 'Use update_bag when it has more than one' bag. It also flags the dial_category change as having downstream consequences on engine advice and asks for a rationale note, which is real decision guidance rather than restatement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_grinderUpdate a grinderA
Destructive
Inspect

Correct a registered grinder: its name, burrs, collar scheme, nominal step, layout, range or notes. Only the keys you send in fields change; send null to clear an optional field (an empty string also clears a text field, but not a number or a closed list). Required fields can be changed but not cleared. fields may be omitted when only reason is sent. setting_scheme, nominal_step and components_spec describe the collar every recorded grind label is read through: call with dry_run first to see which recorded settings would read as a different position, and tell the user before saving. Saving records the change in the grinder's maintenance history; when a locked recipe's setting moves it also opens an unmeasured calibration epoch (set_maintenance_offset resolves it). The collar's current setting is marked assumed when it reads differently, or cleared when the new collar cannot read it, and measured offsets are converted when a click changes worth. Changing direction_convention is a correction: it moves no number, but every finer/coarser said about this grinder was the wrong way round.

NameRequiredDescriptionDefault
fieldsNo
reasonNoOptional note, appended to this record's own notes as an ordinary line. Omit it and nothing is written.
dry_runNotrue: write nothing, and answer with what the edit would do to the settings already recorded (moved[], checked, epoch, position_moved, position_cleared, rescaled_offsets, resigned_offsets, step_ignored, direction_flipped, recipe_count)
grinder_idYesThis account's own grinder number, from list_grinders. Never guess one.

Output Schema

NameRequiredDescription
epochYes
movedYes
statusYes
checkedYes
dry_runYes
messageYes
entry_idYes
grinder_idYes
unreadableYes
moved_countYes
changed_keysYes
recipe_countYes
step_ignoredYes
reads_changedYes
position_movedYes
untranslatableYes
position_clearedYes
rescaled_offsetsYes
resigned_offsetsYes
direction_flippedYes
untranslatable_recipesYes

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far exceeds the annotations: it explains that only sent keys change, that null (or empty string for text) clears, that required fields can be changed but not cleared, that saving writes maintenance history, opens an unmeasured calibration epoch for locked recipes, marks or clears the collar's current setting, converts measured offsets when a click changes worth, and that direction_convention flips all prior finer/coarser statements without moving a number. This is exactly the destructive/consequential context the destructiveHint flag cannot convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and the clearing rule, then layers consequences. Dense and mostly earning its length, though the final sentence about direction_convention is long and the middle section packs several distinct behaviors into one sentence, which slightly hurts scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, non-idempotent nested-object mutation, it covers write semantics, clearing rules, side effects, dry-run behavior, and escalation to a sibling. An output schema exists and the dry_run parameter already enumerates returned fields, so return values need no further explanation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 75%, and the description adds genuine semantics the schema does not: partial-update/patch behavior, null-vs-empty-string clearing rules per type, the ability to omit fields when only reason is sent, and the shared meaning of setting_scheme/nominal_step/components_spec as the lens through which recorded grind labels are read. It does not individually explain every field, but the highest-risk ones are covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ("Correct a registered grinder") and enumerates the mutable aspects (name, burrs, collar scheme, nominal step, layout, range, notes). It is clearly a mutation of an existing record, implicitly distinguished from register_grinder, but it never names a sibling explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete operational guidance: call with dry_run first when changing setting_scheme/nominal_step/components_spec, and tell the user before saving. It also routes to set_maintenance_offset for resolving an opened calibration epoch. What's missing is an explicit boundary against register_grinder or update_machine for adjacent cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_machineUpdate a machineA
Destructive
Inspect

Correct a registered machine (its name, control type, prep type and style, basket, boiler, accessories or notes), or record where its selectors stand (settings). Only the keys you send in fields change; send null to clear an optional field (an empty string also clears a text field, but not a number or a closed list). Required fields can be changed but not cleared. fields may be omitted when only reason is sent. prep_style decides whether shots on this machine are diagnosed at all: only "espresso" is. Programs are edited with register_program, the built-in grinder link is not editable here, and archiving is set_equipment_archived.

NameRequiredDescriptionDefault
fieldsNo
reasonNoOptional note, appended to this record's own notes as an ordinary line. Omit it and nothing is written.
machine_idYesThis account's own machine number, from list_machines. Never guess one.

Output Schema

NameRequiredDescription
statusYes
messageYes
machine_idYes
changed_keysYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations give destructiveHint=true without explaining scope, and the description fills that gap precisely: partial-update semantics ('Only the keys you send in fields change'), the null-vs-empty-string clearing rule and its exception for numbers and closed lists, and the fact that required fields can be changed but not cleared. It also discloses a cross-system side effect not visible anywhere else — prep_style determines whether shots are diagnosed at all.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Highly front-loaded: purpose, then update/clearing semantics, then exceptions, then sibling routing. The parenthetical field list and the dense closing sentence are efficient but pack several rules into single run-on clauses, which costs a little readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a nested-object mutation with an output schema, the description covers what an agent actually needs: how partial updates work, how to clear values, what cannot be cleared, what is out of scope, and the domain consequence of prep_style. Return values are correctly left to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 67% schema coverage the schema already documents most fields, but the description adds rules the schema does not: the null/empty-string clearing asymmetry, the distinction between changing versus clearing required fields, and the consequence of prep_style on diagnosis. It does not, however, restate which fields are required or optional, leaving that to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Correct a registered machine') and enumerates the exact editable surface (name, control type, prep type/style, basket, boiler, accessories, notes) plus the secondary purpose of recording selector positions. It explicitly carves out what belongs to siblings: 'Programs are edited with register_program, the built-in grinder link is not editable here, and archiving is set_equipment_archived.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names three alternatives and the boundary condition for each (register_program for programs, set_equipment_archived for archiving, grinder link out of scope). It also states the degenerate call shape ('fields may be omitted when only reason is sent'), removing the main invocation ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_scaleUpdate a scaleA
Destructive
Inspect

Correct a registered scale: its type, resolution, capacity, capabilities, build or notes. Only the keys you send in fields change; send null to clear an optional field (an empty string also clears a text field, but not a number or a closed list). Required fields can be changed but not cleared. fields may be omitted when only reason is sent. The three capabilities are 1, 0, or null for "the maker never said"; leave one out rather than sending 0 when nobody knows.

NameRequiredDescriptionDefault
fieldsNo
reasonNoOptional note, appended to this record's own notes as an ordinary line. Omit it and nothing is written.
scale_idYesThis account's own scale number, from list_scales. Never guess one.

Output Schema

NameRequiredDescription
statusYes
messageYes
scale_idYes
changed_keysYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With destructiveHint=true and idempotentHint=false already declared, the description earns credit for explaining the actual destruction mechanism: sending null clears optional fields, an empty string clears text but not numbers or closed lists, and required fields can be changed but never cleared. The tri-state capability guidance (1 / 0 / null, and prefer omission over 0) is genuine behavioral context an agent could not infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in one clause, then every following sentence states a distinct rule with no restatement of the name or title. The density is warranted for a 16-field nested patch tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values needn't be described, and annotations plus description together cover the mutation/clearing profile. What remains unaddressed is failure behavior on invalid or foreign scale_id values and any permission or ownership constraints beyond the schema's brief note.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%, and the description compensates strongly: it explains patch semantics for the fields object, null-versus-empty-string clearing rules per type, and gives meaning to auto_tare, auto_timer and flow_rate_display, which the schema types only as integer|null with no description. This is meaning well beyond the structured schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ("Correct a registered scale") and enumerates the editable aspects: type, resolution, capacity, capabilities, build, notes. The word "registered" and the corrective framing distinguish it from register_scale and list_scales without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete operating conditions: only keys sent in fields change, fields may be omitted when only reason is sent, and nullable versus required fields behave differently. It doesn't name a sibling alternative (e.g., register_scale for new records) or state prerequisites, so it stops short of full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_shotUpdate a shotA
DestructiveIdempotent
Inspect

Correct fields on an already-logged shot in place, with no need to delete and re-log. Use for fixing a wrong dose/yield/time or grind label, re-filing a shot onto the right bean (bean_id), backfilling rating/tasting notes, or fixing the timestamp (pulled_at). Changing grind_label re-derives the numeric grind position from the shot's grinder; changing yield/time/dose/tds keeps flow rate and extraction yield consistent automatically.

NameRequiredDescriptionDefault
fieldsYesOnly the fields to change
shot_idYesID of the shot to correct

Output Schema

NameRequiredDescription
statusYes
messageYes
shot_idYes
updated_fieldsYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is known. The description adds valuable behavioral context beyond that: it clarifies this is an in-place correction (not a deletion), and it discloses automatic re-derivation of numeric grind position and consistency maintenance for flow rate and extraction yield when changing key fields. Permissions and error behaviors are not addressed, but the core behavioral traits are surfaced.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with zero waste: first states purpose and scope, second lists common use cases, third covers derived-field behavior. The purpose is front-loaded and every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema and annotations present, the description needn't explain return values or safety flags. It covers purpose, use cases, and key behavioral side effects. Minor gaps remain, such as validation constraints (e.g., bag_id must belong to the shot's bean), but those are documented in the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description adds meaning beyond the schema by explaining that changing grind_label re-derives the numeric position and that changing yield/time/dose/tds keeps flow rate and extraction yield consistent. It also calls out bean_id for re-filing, though that is already described in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Correct') and resource ('already-logged shot') and immediately distinguishes itself from delete+re-log. An agent can tell it apart from delete_shot and log_shot without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Enumerates concrete scenarios for use: fixing dose/yield/time/grind, re-filing onto the right bean, backfilling rating/tasting notes, correcting timestamp. It also implies the alternative (delete and re-log) and asserts it is unnecessary, giving clear routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_waterUpdate a water recipeA
Destructive
Inspect

Correct a registered water: its name, type, mineral figures or notes. Only the keys you send in fields change; send null to clear an optional field (an empty string also clears a text field, but not a number or a closed list). Required fields can be changed but not cleared. fields may be omitted when only reason is sent.

NameRequiredDescriptionDefault
fieldsNo
reasonNoOptional note, appended to this record's own notes as an ordinary line. Omit it and nothing is written.
water_idYesThis account's own water number, from list_waters. Never guess one.

Output Schema

NameRequiredDescription
statusYes
messageYes
water_idYes
changed_keysYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial mutation semantics beyond the annotations: only supplied keys change, null clears optional fields, empty string clears text but not numbers or closed lists, and required fields can be changed but not cleared. These details are important for a destructive, non-idempotent update tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: it first identifies the operation and updatable aspects, then immediately covers mutation and clearing rules. Every sentence adds actionable information without repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the annotations, nested parameters, and existing output schema, the description supplies the remaining critical mutation behavior an agent needs to call it correctly. Return values are covered by the output schema, so no description space is wasted there.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only 67% schema coverage and a nested fields object, the description compensates by explaining partial-update behavior, null-versus-empty-string semantics, required-field constraints, and when fields can be omitted. This meaningfully supplements the type-only nested schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: correcting a registered water's name, type, mineral figures, or notes. It implies the record must already exist, which distinguishes it from register_water, but it does not explicitly name that sibling or other alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'Correct a registered water' and by the note that fields may be omitted when only reason is sent. However, it does not explicitly state when to use this versus register_water or list_waters, and the water_id guidance lives in the schema rather than the description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updates
    • Changedget_dial_state3 fields changed
      • changedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / done / type
        Previous value: -"integer"New value: +"number"
      • changedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / every / type
        Previous value: -"integer"New value: +"number"
      • changedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / remaining / type
        Previous value: -"integer"New value: +"number"
    • Changedlist_machines5 fields changed
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / properties / done / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "null",
        +  "number",
        +  "integer"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / properties / every / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "null",
        +  "number",
        +  "integer"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / properties / remaining / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "null",
        +  "number",
        +  "integer"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / schedule / properties / every / required
        Previous value: -[
        -  "days",
        -  "litres"
        -]New value: +[
        +  "litres"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / schedule / properties / muted_until / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedlist_maintenance_due3 fields changed
      • changedOutput schema / properties / items / items / properties / done / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "number",
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / items / items / properties / every / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "number",
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / items / items / properties / remaining / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "number",
        +  "integer",
        +  "null"
        +]
    • Changedset_maintenance_schedule8 fields changed
      • changedOutput schema / properties / due / properties / done / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / due / properties / due_at / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / due / properties / every / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / due / properties / last_at / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / due / properties / overdue_at / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / due / properties / ratio / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / due / properties / remaining / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / due / properties / soon_at / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
  2. 21 tool updates
    • Changedcompute_age2 fields changed
      • addedOutput schema / properties / verdict_code
        Added value: +{
        +  "type": "string"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "bean_id",
        -  "bag_id",
        -  "roast_date",
        -  "age_days",
        -  "days_off_roast",
        -  "days_since_opened",
        -  "frozen_days_excluded",
        -  "effective_age_days",
        -  "rest_window",
        -  "verdict",
        -  "grams_remaining",
        -  "warnings"
        -]New value: +[
        +  "bean_id",
        +  "bag_id",
        +  "roast_date",
        +  "age_days",
        +  "days_off_roast",
        +  "days_since_opened",
        +  "frozen_days_excluded",
        +  "effective_age_days",
        +  "rest_window",
        +  "verdict",
        +  "verdict_code",
        +  "grams_remaining",
        +  "warnings"
        +]
    • Changeddiagnose_preview3 fields changed
      • changedInput schema / properties / source / description
        Previous value: -"Grinder position source — affects G1 warning (raw mode only)"New value: +"Grinder position source. Affects G1 warning (raw mode only)"
      • addedOutput schema / properties / warning_codes
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "category",
        -  "facts",
        -  "hypotheses",
        -  "warnings",
        -  "one_variable_only",
        -  "kb_version",
        -  "bean_context"
        -]New value: +[
        +  "category",
        +  "facts",
        +  "hypotheses",
        +  "warnings",
        +  "one_variable_only",
        +  "kb_version",
        +  "warning_codes",
        +  "bean_context"
        +]
    • Changeddiagnose_shot2 fields changed
      • addedOutput schema / properties / warning_codes
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "category",
        -  "facts",
        -  "hypotheses",
        -  "warnings",
        -  "one_variable_only",
        -  "kb_version",
        -  "bean_context"
        -]New value: +[
        +  "category",
        +  "facts",
        +  "hypotheses",
        +  "warnings",
        +  "one_variable_only",
        +  "kb_version",
        +  "warning_codes",
        +  "bean_context"
        +]
    • Changedget_dial_state10 fields changed
      • addedOutput schema / properties / active_coffee / properties / age_verdict_code
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / active_coffee / properties / bag / properties / age_verdict_code
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / active_coffee / properties / bag / properties / lot_revision_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / active_coffee / properties / bag / properties / product_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / active_coffee / properties / bag / required
        Previous value: -[
        -  "id",
        -  "bean_id",
        -  "roast_date",
        -  "buy_date",
        -  "opened_date",
        -  "best_by_date",
        -  "bag_weight_g",
        -  "weight_adjustment_g",
        -  "cost",
        -  "currency",
        -  "state",
        -  "finished",
        -  "frozen_at",
        -  "unfrozen_at",
        -  "frozen_storage",
        -  "frozen_note",
        -  "storage",
        -  "ean",
        -  "qr_code",
        -  "bag_notes",
        -  "archived",
        -  "days_off_roast",
        -  "days_since_opened",
        -  "frozen_days_excluded",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining"
        -]New value: +[
        +  "id",
        +  "bean_id",
        +  "roast_date",
        +  "buy_date",
        +  "opened_date",
        +  "best_by_date",
        +  "bag_weight_g",
        +  "weight_adjustment_g",
        +  "cost",
        +  "currency",
        +  "state",
        +  "finished",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "storage",
        +  "ean",
        +  "qr_code",
        +  "bag_notes",
        +  "archived",
        +  "product_id",
        +  "lot_revision_id",
        +  "days_off_roast",
        +  "days_since_opened",
        +  "frozen_days_excluded",
        +  "effective_age_days",
        +  "age_verdict",
        +  "age_verdict_code",
        +  "grams_remaining"
        +]
      • addedOutput schema / properties / active_coffee / properties / catalog_coffee_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / active_coffee / properties / roaster / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / active_coffee / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "roaster",
        -  "origin",
        -  "variety",
        -  "roast_date",
        -  "roast_level",
        -  "process_type",
        -  "dial_category",
        -  "bag_notes",
        -  "rest_window_days_min",
        -  "rest_window_days_max",
        -  "state",
        -  "opened_date",
        -  "storage",
        -  "is_active",
        -  "weight_adjustment_g",
        -  "buy_date",
        -  "bag_weight_g",
        -  "cost",
        -  "currency",
        -  "decaffeinated",
        -  "bean_mix",
        -  "roast_range",
        -  "roast_custom",
        -  "roasting_type",
        -  "aromatics",
        -  "cupping_points",
        -  "rating",
        -  "favourite",
        -  "url",
        -  "ean",
        -  "qr_code",
        -  "co2e_kg",
        -  "attachments",
        -  "archived",
        -  "frozen_at",
        -  "unfrozen_at",
        -  "frozen_storage",
        -  "frozen_note",
        -  "best_by_date",
        -  "finished",
        -  "notes",
        -  "arabica_pct",
        -  "body",
        -  "acidity",
        -  "sweetness",
        -  "age_days",
        -  "days_off_roast",
        -  "days_since_opened",
        -  "frozen_days_excluded",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining",
        -  "current_bag_id",
        -  "bag"
        -]New value: +[
        +  "id",
        +  "name",
        +  "roaster",
        +  "origin",
        +  "variety",
        +  "roast_date",
        +  "roast_level",
        +  "process_type",
        +  "dial_category",
        +  "bag_notes",
        +  "rest_window_days_min",
        +  "rest_window_days_max",
        +  "state",
        +  "opened_date",
        +  "storage",
        +  "is_active",
        +  "weight_adjustment_g",
        +  "buy_date",
        +  "bag_weight_g",
        +  "cost",
        +  "currency",
        +  "decaffeinated",
        +  "bean_mix",
        +  "roast_range",
        +  "roast_custom",
        +  "roasting_type",
        +  "aromatics",
        +  "cupping_points",
        +  "rating",
        +  "favourite",
        +  "url",
        +  "ean",
        +  "qr_code",
        +  "co2e_kg",
        +  "attachments",
        +  "archived",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "best_by_date",
        +  "finished",
        +  "notes",
        +  "arabica_pct",
        +  "body",
        +  "acidity",
        +  "sweetness",
        +  "catalog_coffee_id",
        +  "age_days",
        +  "days_off_roast",
        +  "days_since_opened",
        +  "frozen_days_excluded",
        +  "effective_age_days",
        +  "age_verdict",
        +  "age_verdict_code",
        +  "grams_remaining",
        +  "current_bag_id",
        +  "bag"
        +]
      • addedOutput schema / properties / active_machine / properties / prep_style / type
        Added value: +"string"
      • addedOutput schema / properties / active_machine / properties / prep_type / type
        Added value: +"string"
    • Changedget_maintenance_guides1 field changed
      • changedInput schema / properties / guide_ids / description
        Previous value: -"Optional — the guides to fetch. Omit for every guide the user's equipment can use"New value: +"Optional: the guides to fetch. Omit for every guide the user's equipment can use"
    • Changedlist_beans10 fields changed
      • addedOutput schema / properties / beans / items / properties / age_verdict_code
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / beans / items / properties / bags / items / properties / age_verdict_code
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / beans / items / properties / bags / items / properties / lot_revision_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / beans / items / properties / bags / items / properties / product_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / beans / items / properties / bags / items / required
        Previous value: -[
        -  "id",
        -  "bean_id",
        -  "roast_date",
        -  "buy_date",
        -  "opened_date",
        -  "best_by_date",
        -  "bag_weight_g",
        -  "weight_adjustment_g",
        -  "cost",
        -  "currency",
        -  "state",
        -  "finished",
        -  "frozen_at",
        -  "unfrozen_at",
        -  "frozen_storage",
        -  "frozen_note",
        -  "storage",
        -  "ean",
        -  "qr_code",
        -  "bag_notes",
        -  "archived",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining",
        -  "days_since_opened",
        -  "days_off_roast",
        -  "frozen_days_excluded"
        -]New value: +[
        +  "id",
        +  "bean_id",
        +  "roast_date",
        +  "buy_date",
        +  "opened_date",
        +  "best_by_date",
        +  "bag_weight_g",
        +  "weight_adjustment_g",
        +  "cost",
        +  "currency",
        +  "state",
        +  "finished",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "storage",
        +  "ean",
        +  "qr_code",
        +  "bag_notes",
        +  "archived",
        +  "product_id",
        +  "lot_revision_id",
        +  "effective_age_days",
        +  "age_verdict",
        +  "age_verdict_code",
        +  "grams_remaining",
        +  "days_since_opened",
        +  "days_off_roast",
        +  "frozen_days_excluded"
        +]
      • addedOutput schema / properties / beans / items / properties / catalog_coffee_id
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / beans / items / properties / origins / items / properties / country_code
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / beans / items / properties / origins / items / required
        Previous value: -[
        -  "country",
        -  "region",
        -  "farm",
        -  "farmer",
        -  "variety",
        -  "processing",
        -  "elevation_m",
        -  "elevation_max_m",
        -  "harvest",
        -  "percentage"
        -]New value: +[
        +  "country",
        +  "country_code",
        +  "region",
        +  "farm",
        +  "farmer",
        +  "variety",
        +  "processing",
        +  "elevation_m",
        +  "elevation_max_m",
        +  "harvest",
        +  "percentage"
        +]
      • changedOutput schema / properties / beans / items / properties / roaster / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "roaster",
        -  "roast_level",
        -  "archived",
        -  "favourite",
        -  "decaffeinated",
        -  "process_type",
        -  "dial_category",
        -  "rest_window_days_min",
        -  "rest_window_days_max",
        -  "rating",
        -  "aromatics",
        -  "cupping_points",
        -  "roasting_type",
        -  "variety",
        -  "origin",
        -  "url",
        -  "notes",
        -  "arabica_pct",
        -  "body",
        -  "acidity",
        -  "sweetness",
        -  "roast_date",
        -  "buy_date",
        -  "opened_date",
        -  "best_by_date",
        -  "bag_weight_g",
        -  "weight_adjustment_g",
        -  "cost",
        -  "currency",
        -  "state",
        -  "finished",
        -  "frozen_at",
        -  "unfrozen_at",
        -  "frozen_storage",
        -  "frozen_note",
        -  "storage",
        -  "ean",
        -  "qr_code",
        -  "bag_notes",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining",
        -  "days_since_opened",
        -  "days_off_roast",
        -  "frozen_days_excluded",
        -  "current_bag_id",
        -  "bags",
        -  "origins"
        -]New value: +[
        +  "id",
        +  "name",
        +  "roaster",
        +  "catalog_coffee_id",
        +  "roast_level",
        +  "archived",
        +  "favourite",
        +  "decaffeinated",
        +  "process_type",
        +  "dial_category",
        +  "rest_window_days_min",
        +  "rest_window_days_max",
        +  "rating",
        +  "aromatics",
        +  "cupping_points",
        +  "roasting_type",
        +  "variety",
        +  "origin",
        +  "url",
        +  "notes",
        +  "arabica_pct",
        +  "body",
        +  "acidity",
        +  "sweetness",
        +  "roast_date",
        +  "buy_date",
        +  "opened_date",
        +  "best_by_date",
        +  "bag_weight_g",
        +  "weight_adjustment_g",
        +  "cost",
        +  "currency",
        +  "state",
        +  "finished",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "storage",
        +  "ean",
        +  "qr_code",
        +  "bag_notes",
        +  "effective_age_days",
        +  "age_verdict",
        +  "age_verdict_code",
        +  "grams_remaining",
        +  "days_since_opened",
        +  "days_off_roast",
        +  "frozen_days_excluded",
        +  "current_bag_id",
        +  "bags",
        +  "origins"
        +]
    • Changedlist_grinders1 field changed
      • changedInput schema / properties / maintenance / description
        Previous value: -"How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words — quote, source, alternates, the interval as written."New value: +"How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words (quote, source, alternates, the interval as written)."
    • Changedlist_machines3 fields changed
      • changedInput schema / properties / maintenance / description
        Previous value: -"How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words — quote, source, alternates, the interval as written."New value: +"How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words (quote, source, alternates, the interval as written)."
      • changedOutput schema / properties / machines / items / properties / prep_style / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
      • changedOutput schema / properties / machines / items / properties / prep_type / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +"string"
    • Changedlist_recipes1 field changed
      • changedOutput schema / properties / recipes / items / properties / roaster_name / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedlog_maintenance3 fields changed
      • changedInput schema / properties / disturbs_zero / description
        Previous value: -"Whether this job disturbed the reference the stored numbers are read against — for a burr clean, whether the burrs actually came out. Defaults to the task's own answer; send 0 for a wipe-down that left the collar alone"New value: +"Whether this job disturbed the reference the stored numbers are read against: for a burr clean, whether the burrs actually came out. Defaults to the task's own answer; send 0 for a wipe-down that left the collar alone"
      • changedInput schema / properties / guide_id / description
        Previous value: -"Optional — the id of the maintenance guide that was followed. Must be a guide for this task"New value: +"Optional: the id of the maintenance guide that was followed. Must be a guide for this task"
      • changedInput schema / properties / note / description
        Previous value: -"Optional — what you did, and anything that surprised you"New value: +"Optional: what you did, and anything that surprised you"
    • Changedlog_shot3 fields changed
      • changedInput schema / properties / data_confidence / description
        Previous value: -"measured = weighed/timed live; recalled = from memory; estimated = a best guess (e.g. missed the timer — \"~25s\")"New value: +"measured = weighed/timed live; recalled = from memory; estimated = a best guess (e.g. \"~25s\" after missing the timer)"
      • changedInput schema / properties / pulled_at / description
        Previous value: -"When the shot was actually pulled (ISO 8601 or \"YYYY-MM-DD HH:MM:SS\"), for backdated entries. Defaults to now (null = now). When backdating, pass the returned shot_id to diagnose_shot explicitly — the default diagnosis target is the chronologically latest shot."New value: +"When the shot was actually pulled (ISO 8601 or \"YYYY-MM-DD HH:MM:SS\"), for backdated entries. Defaults to now (null = now). When backdating, pass the returned shot_id to diagnose_shot explicitly, because the default diagnosis target is the chronologically latest shot."
      • changedInput schema / properties / tasted / description
        Previous value: -"1 = the shot was tasted at log time. The explicit way to record a CLEAN tasting: sensory tags all describe defects, so a good shot has none — without this flag (or a rating) it would be filed taste-pending. Tags or a rating also mark a shot as tasted; 0 (default) = taste later."New value: +"1 = the shot was tasted at log time. The explicit way to record a CLEAN tasting: sensory tags all describe defects, so a good shot has none. Without this flag (or a rating) it would be filed taste-pending. Tags or a rating also mark a shot as tasted; 0 (default) = taste later."
    • Changedregister_bag7 fields changed
      • changedInput schema / properties / best_by_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / buy_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • addedInput schema / properties / catalog_coffee_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • addedInput schema / properties / lot_revision_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • changedInput schema / properties / opened_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • addedInput schema / properties / product_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • changedInput schema / properties / roast_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
    • Changedregister_coffee10 fields changed
      • changedInput schema / properties / best_by_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / buy_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • addedInput schema / properties / catalog_coffee_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • changedInput schema / properties / frozen_at / description
        Previous value: -"Calendar day the bag went into the freezer, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag went into the freezer, YYYY-MM-DD: a day, not a timestamp"
      • addedInput schema / properties / lot_revision_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • changedInput schema / properties / notes / description
        Previous value: -"The coffee's own description — the roaster's tasting notes, the dialing approach that worked. Per bag notes go in bag_notes."New value: +"The coffee's own description: the roaster's tasting notes, the dialing approach that worked. Per bag notes go in bag_notes."
      • changedInput schema / properties / opened_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • addedInput schema / properties / product_id
        Added value: +{
        +  "description": "Set by the apps. Leave out.",
        +  "type": "string"
        +}
      • changedInput schema / properties / roast_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / unfrozen_at / description
        Previous value: -"Calendar day the bag came out, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag came out, YYYY-MM-DD: a day, not a timestamp"
    • Changedregister_grinder1 field changed
      • changedInput schema / properties / catalog_id / description
        Previous value: -"Optional — the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog."New value: +"Optional: the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog."
    • Changedregister_machine3 fields changed
      • changedInput schema / properties / catalog_id / description
        Previous value: -"Optional — the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog."New value: +"Optional: the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog."
      • changedInput schema / properties / hardness_dh / description
        Previous value: -"Optional — the TOTAL hardness (GH) of the water this machine runs on, in °dH (German degrees; ppm as CaCO3 ÷ 17.85), as a test strip reports it. Picks the band of a maker's hardness table for descaling and filter life"New value: +"Optional: the TOTAL hardness (GH) of the water this machine runs on, in °dH (German degrees; ppm as CaCO3 ÷ 17.85), as a test strip reports it. Picks the band of a maker's hardness table for descaling and filter life"
      • changedInput schema / properties / prep_style / description
        Previous value: -"Preparation style. One of: espresso, filter, immersion, percolation. Only \"espresso\" is diagnosed by the dialing engine (G8) — anything else is recorded but never dialed, so do not guess this field."New value: +"Preparation style. One of: espresso, filter, immersion, percolation. Only \"espresso\" is diagnosed by the dialing engine (G8). Anything else is recorded but never dialed, so do not guess this field."
    • Changedregister_scale3 fields changed
      • changedInput schema / properties / height_mm / description
        Previous value: -"Overall height in mm — the dimension that decides whether it fits under a group head"New value: +"Overall height in mm: the dimension that decides whether it fits under a group head"
      • changedInput schema / properties / resolution_banded / description
        Previous value: -"1 when resolution_g only holds in the lowest weight band and coarsens with load — put the ladder in notes"New value: +"1 when resolution_g only holds in the lowest weight band and coarsens with load. Put the ladder in notes"
      • changedInput schema / properties / width_mm / description
        Previous value: -"Platform width in mm — measure your drip tray"New value: +"Platform width in mm. Measure your drip tray"
    • Changedset_maintenance_schedule1 field changed
      • changedInput schema / properties / changed_at / description
        Previous value: -"Optional — when the user made this change (ISO 8601, UTC). An app sending a queued edit passes it, so an older edit never overwrites a newer one; it is then answered with applied: false"New value: +"Optional: when the user made this change (ISO 8601, UTC). An app sending a queued edit passes it, so an older edit never overwrites a newer one; it is then answered with applied: false"
    • Changedsuggest_next_step2 fields changed
      • addedOutput schema / properties / warning_codes
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "next_step",
        -  "category",
        -  "warnings"
        -]New value: +[
        +  "next_step",
        +  "category",
        +  "warnings",
        +  "warning_codes"
        +]
    • Changedupdate_bag6 fields changed
      • changedInput schema / properties / fields / properties / best_by_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / buy_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / frozen_at / description
        Previous value: -"Calendar day the bag went into the freezer, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag went into the freezer, YYYY-MM-DD: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / opened_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / roast_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / unfrozen_at / description
        Previous value: -"Calendar day the bag came out, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag came out, YYYY-MM-DD: a day, not a timestamp"
    • Changedupdate_bean8 fields changed
      • changedInput schema / properties / fields / properties / best_by_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / buy_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / frozen_at / description
        Previous value: -"Calendar day the bag went into the freezer, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag went into the freezer, YYYY-MM-DD: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / notes / description
        Previous value: -"The coffee's own description — roaster tasting notes, the dialing approach. Replaces the stored value; the optional reason is appended instead."New value: +"The coffee's own description: roaster tasting notes, the dialing approach. Replaces the stored value; the optional reason is appended instead."
      • changedInput schema / properties / fields / properties / opened_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / roast_date / description
        Previous value: -"Calendar day in YYYY-MM-DD format — a day, not a timestamp"New value: +"Calendar day in YYYY-MM-DD format: a day, not a timestamp"
      • changedInput schema / properties / fields / properties / unfrozen_at / description
        Previous value: -"Calendar day the bag came out, YYYY-MM-DD — a day, not a timestamp"New value: +"Calendar day the bag came out, YYYY-MM-DD: a day, not a timestamp"
      • changedInput schema / properties / reason / description
        Previous value: -"Optional free-text note, appended as an ordinary line to the coffee's notes — or to the current bag's bag_notes when the call changed only bag fields. Omit it and nothing is written."New value: +"Optional free-text note, appended as an ordinary line to the coffee's notes, or to the current bag's bag_notes when the call changed only bag fields. Omit it and nothing is written."
    • Changedupdate_grinder2 fields changed
      • changedInput schema / properties / fields / properties / components_spec / description
        Previous value: -"JSON array of {name, per_step}, coarsest first — the ring arithmetic of a compound collar, or the ordered settings of a named one"New value: +"JSON array of {name, per_step}, coarsest first: the ring arithmetic of a compound collar, or the ordered settings of a named one"
      • changedInput schema / properties / fields / properties / nominal_step / description
        Previous value: -"Smallest adjustment increment. On a numeric_stepless collar it is the precision labels are recorded in (0.1 records tenths) and the size of one click. numeric_stepped and clicks_single collars record whole steps whatever it says — to record tenths, set setting_scheme numeric_stepless as well"New value: +"Smallest adjustment increment. On a numeric_stepless collar it is the precision labels are recorded in (0.1 records tenths) and the size of one click. numeric_stepped and clicks_single collars record whole steps whatever it says. To record tenths, set setting_scheme numeric_stepless as well"
  3. 5 tool updates
    • Changedget_dial_state6 fields changed
      • addedOutput schema / properties / active_machine / properties / settings / items / properties / provenance
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "default_quote": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "default_source": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "quote": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "source": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    }
        +  },
        +  "required": [
        +    "source",
        +    "quote",
        +    "default_source",
        +    "default_quote"
        +  ],
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / active_machine / properties / settings / items / required
        Previous value: -[
        -  "key",
        -  "label",
        -  "type",
        -  "options",
        -  "min",
        -  "max",
        -  "step",
        -  "unit",
        -  "default",
        -  "value"
        -]New value: +[
        +  "key",
        +  "label",
        +  "type",
        +  "options",
        +  "min",
        +  "max",
        +  "step",
        +  "unit",
        +  "default",
        +  "value",
        +  "provenance"
        +]
      • addedOutput schema / properties / locked_recipe / properties / machine_settings
        Added value: +{
        +  "additionalProperties": true,
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / locked_recipe / required
        Previous value: -[
        -  "id",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "status",
        -  "grind_label",
        -  "grind_position_value",
        -  "dose_g",
        -  "target_yield_g",
        -  "target_time_s",
        -  "finish_action",
        -  "drink_intent",
        -  "profile",
        -  "kb_version",
        -  "locked_at",
        -  "grind_reading"
        -]New value: +[
        +  "id",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "status",
        +  "grind_label",
        +  "grind_position_value",
        +  "dose_g",
        +  "target_yield_g",
        +  "target_time_s",
        +  "finish_action",
        +  "drink_intent",
        +  "profile",
        +  "kb_version",
        +  "locked_at",
        +  "machine_settings",
        +  "grind_reading"
        +]
      • addedOutput schema / properties / recent_shots / items / properties / machine_settings / additionalProperties
        Added value: +true
      • addedOutput schema / properties / recent_shots / items / properties / machine_settings / type
        Added value: +[
        +  "object",
        +  "null"
        +]
    • Changedlist_machines2 fields changed
      • addedOutput schema / properties / machines / items / properties / settings / items / properties / provenance
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "default_quote": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "default_source": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "quote": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "source": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    }
        +  },
        +  "required": [
        +    "source",
        +    "quote",
        +    "default_source",
        +    "default_quote"
        +  ],
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / machines / items / properties / settings / items / required
        Previous value: -[
        -  "key",
        -  "label",
        -  "type",
        -  "options",
        -  "min",
        -  "max",
        -  "step",
        -  "unit",
        -  "default",
        -  "value"
        -]New value: +[
        +  "key",
        +  "label",
        +  "type",
        +  "options",
        +  "min",
        +  "max",
        +  "step",
        +  "unit",
        +  "default",
        +  "value",
        +  "provenance"
        +]
    • Changedlist_recipes2 fields changed
      • addedOutput schema / properties / recipes / items / properties / machine_settings
        Added value: +{
        +  "additionalProperties": true,
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / recipes / items / required
        Previous value: -[
        -  "id",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "status",
        -  "grind_label",
        -  "grind_position_value",
        -  "dose_g",
        -  "target_yield_g",
        -  "target_time_s",
        -  "finish_action",
        -  "drink_intent",
        -  "profile",
        -  "locked_at",
        -  "bean_name",
        -  "roaster_name",
        -  "grinder_name",
        -  "machine_name",
        -  "grind_reading"
        -]New value: +[
        +  "id",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "status",
        +  "grind_label",
        +  "grind_position_value",
        +  "dose_g",
        +  "target_yield_g",
        +  "target_time_s",
        +  "finish_action",
        +  "drink_intent",
        +  "profile",
        +  "locked_at",
        +  "machine_settings",
        +  "bean_name",
        +  "roaster_name",
        +  "grinder_name",
        +  "machine_name",
        +  "grind_reading"
        +]
    • Changedlist_shots2 fields changed
      • addedOutput schema / properties / shots / items / properties / machine_settings / additionalProperties
        Added value: +true
      • changedOutput schema / properties / shots / items / properties / machine_settings / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "object",
        +  "null"
        +]
    • Changedset_active1 field changed
      • changedInput schema / properties / program_id / description
        Previous value: -"A program number from the active machine's list. null = leave unchanged"New value: +"A program number from the active machine's list; switching machines clears it. null = leave unchanged"
  4. 7 tool updates
    • Changedget_dial_state4 fields changed
      • addedOutput schema / properties / active_machine / properties / settings
        Added value: +{
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "default": {
        +        "type": [
        +          "number",
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "key": {
        +        "type": "string"
        +      },
        +      "label": {
        +        "type": "string"
        +      },
        +      "max": {
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "min": {
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "options": {
        +        "items": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "label": {
        +              "type": "string"
        +            },
        +            "meaning": {
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "value": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "value",
        +            "label",
        +            "meaning"
        +          ],
        +          "type": "object"
        +        },
        +        "type": [
        +          "null",
        +          "array"
        +        ]
        +      },
        +      "step": {},
        +      "type": {
        +        "type": "string"
        +      },
        +      "unit": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "value": {
        +        "type": [
        +          "number",
        +          "null",
        +          "string"
        +        ]
        +      }
        +    },
        +    "required": [
        +      "key",
        +      "label",
        +      "type",
        +      "options",
        +      "min",
        +      "max",
        +      "step",
        +      "unit",
        +      "default",
        +      "value"
        +    ],
        +    "type": "object"
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / active_machine / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "control_type",
        -  "basket_size_grams",
        -  "boiler_type",
        -  "notes",
        -  "is_active",
        -  "prep_type",
        -  "prep_style",
        -  "archived",
        -  "photo",
        -  "tools",
        -  "connected_device",
        -  "built_in_grinder_id",
        -  "catalog_id",
        -  "hardness_dh"
        -]New value: +[
        +  "id",
        +  "name",
        +  "control_type",
        +  "basket_size_grams",
        +  "boiler_type",
        +  "notes",
        +  "is_active",
        +  "prep_type",
        +  "prep_style",
        +  "archived",
        +  "photo",
        +  "tools",
        +  "connected_device",
        +  "built_in_grinder_id",
        +  "catalog_id",
        +  "hardness_dh",
        +  "settings"
        +]
      • addedOutput schema / properties / recent_shots / items / properties / machine_settings
        Added value: +{}
      • changedOutput schema / properties / recent_shots / items / required
        Previous value: -[
        -  "id",
        -  "pulled_at",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "grind_label",
        -  "grind_components",
        -  "grind_position_value",
        -  "dose_g",
        -  "yield_g",
        -  "time_s",
        -  "flow_rate_gps",
        -  "finish_action",
        -  "drink_intent",
        -  "flavor_notes",
        -  "sensory_tags",
        -  "observations",
        -  "temp_taste",
        -  "rating",
        -  "verdict",
        -  "data_confidence",
        -  "temperature_c",
        -  "bloom_time_s",
        -  "first_drip_time_s",
        -  "beverage_g",
        -  "tds",
        -  "ey_pct",
        -  "water_id",
        -  "vessel_name",
        -  "vessel_weight_g",
        -  "favourite",
        -  "best_brew",
        -  "tasted",
        -  "bag_id",
        -  "taste_pending"
        -]New value: +[
        +  "id",
        +  "pulled_at",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "grind_label",
        +  "grind_components",
        +  "grind_position_value",
        +  "dose_g",
        +  "yield_g",
        +  "time_s",
        +  "flow_rate_gps",
        +  "finish_action",
        +  "drink_intent",
        +  "flavor_notes",
        +  "sensory_tags",
        +  "observations",
        +  "temp_taste",
        +  "rating",
        +  "verdict",
        +  "data_confidence",
        +  "temperature_c",
        +  "bloom_time_s",
        +  "first_drip_time_s",
        +  "beverage_g",
        +  "tds",
        +  "ey_pct",
        +  "water_id",
        +  "vessel_name",
        +  "vessel_weight_g",
        +  "favourite",
        +  "best_brew",
        +  "tasted",
        +  "bag_id",
        +  "machine_settings",
        +  "taste_pending"
        +]
    • Changedget_maintenance_guides1 field changed
      • addedOutput schema / properties / guides / items / properties / steps / items / properties / repeat
        Added value: +{
        +  "type": "integer"
        +}
    • Changedlist_machines13 fields changed
      • changedOutput schema / properties / machines / items / properties / catalog_id / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / catalog / properties / brand / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / catalog / properties / id / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / catalog / properties / model / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / type
        Previous value: -[
        -  "null",
        -  "object"
        -]New value: +[
        +  "object",
        +  "null"
        +]
      • addedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / also_covers / items
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / also_covers / type
        Added value: +[
        +  "array",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / duration_minutes / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / machine_alert / type
        Previous value: -[
        -  "object",
        -  "null"
        -]New value: +[
        +  "null",
        +  "object"
        +]
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / status / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / notes / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • addedOutput schema / properties / machines / items / properties / settings
        Added value: +{
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "default": {
        +        "type": [
        +          "number",
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "key": {
        +        "type": "string"
        +      },
        +      "label": {
        +        "type": "string"
        +      },
        +      "max": {
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "min": {
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "options": {
        +        "items": {
        +          "additionalProperties": true,
        +          "properties": {
        +            "label": {
        +              "type": "string"
        +            },
        +            "meaning": {
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "value": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "value",
        +            "label",
        +            "meaning"
        +          ],
        +          "type": "object"
        +        },
        +        "type": [
        +          "null",
        +          "array"
        +        ]
        +      },
        +      "step": {},
        +      "type": {
        +        "type": "string"
        +      },
        +      "unit": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "value": {
        +        "type": [
        +          "number",
        +          "null",
        +          "string"
        +        ]
        +      }
        +    },
        +    "required": [
        +      "key",
        +      "label",
        +      "type",
        +      "options",
        +      "min",
        +      "max",
        +      "step",
        +      "unit",
        +      "default",
        +      "value"
        +    ],
        +    "type": "object"
        +  },
        +  "type": [
        +    "array",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / machines / items / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "control_type",
        -  "basket_size_grams",
        -  "boiler_type",
        -  "notes",
        -  "prep_type",
        -  "prep_style",
        -  "archived",
        -  "built_in_grinder_id",
        -  "catalog_id",
        -  "hardness_dh",
        -  "tools_list",
        -  "programs",
        -  "maintenance"
        -]New value: +[
        +  "id",
        +  "name",
        +  "control_type",
        +  "basket_size_grams",
        +  "boiler_type",
        +  "notes",
        +  "prep_type",
        +  "prep_style",
        +  "archived",
        +  "built_in_grinder_id",
        +  "catalog_id",
        +  "hardness_dh",
        +  "tools_list",
        +  "programs",
        +  "maintenance",
        +  "settings"
        +]
    • Changedlist_shots2 fields changed
      • addedOutput schema / properties / shots / items / properties / machine_settings
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / shots / items / required
        Previous value: -[
        -  "id",
        -  "pulled_at",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "grind_label",
        -  "grind_components",
        -  "grind_position_value",
        -  "dose_g",
        -  "yield_g",
        -  "time_s",
        -  "flow_rate_gps",
        -  "finish_action",
        -  "drink_intent",
        -  "flavor_notes",
        -  "sensory_tags",
        -  "observations",
        -  "temp_taste",
        -  "rating",
        -  "verdict",
        -  "data_confidence",
        -  "temperature_c",
        -  "bloom_time_s",
        -  "first_drip_time_s",
        -  "beverage_g",
        -  "tds",
        -  "ey_pct",
        -  "water_id",
        -  "vessel_name",
        -  "vessel_weight_g",
        -  "favourite",
        -  "best_brew",
        -  "tasted",
        -  "bag_id",
        -  "bean_name",
        -  "taste_pending"
        -]New value: +[
        +  "id",
        +  "pulled_at",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "grind_label",
        +  "grind_components",
        +  "grind_position_value",
        +  "dose_g",
        +  "yield_g",
        +  "time_s",
        +  "flow_rate_gps",
        +  "finish_action",
        +  "drink_intent",
        +  "flavor_notes",
        +  "sensory_tags",
        +  "observations",
        +  "temp_taste",
        +  "rating",
        +  "verdict",
        +  "data_confidence",
        +  "temperature_c",
        +  "bloom_time_s",
        +  "first_drip_time_s",
        +  "beverage_g",
        +  "tds",
        +  "ey_pct",
        +  "water_id",
        +  "vessel_name",
        +  "vessel_weight_g",
        +  "favourite",
        +  "best_brew",
        +  "tasted",
        +  "bag_id",
        +  "machine_settings",
        +  "bean_name",
        +  "taste_pending"
        +]
    • Changedlog_shot1 field changed
      • addedInput schema / properties / machine_settings
        Added value: +{
        +  "description": "Only when this one shot was pulled with a selector somewhere other than the machine's recorded settings: {key: position}. The shot is otherwise stamped with those automatically; a lasting change goes through update_machine.",
        +  "maxProperties": 16,
        +  "type": "object"
        +}
    • Changedupdate_machine1 field changed
      • addedInput schema / properties / fields / properties / settings
        Added value: +{
        +  "description": "Where the machine's selectors now stand, as {key: position} from list_machines settings, e.g. {\"brew_temperature\": \"ii\"}. Only the keys given change; a null position clears one, null clears all. Shots are stamped with these.",
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
    • Changedupdate_shot1 field changed
      • addedInput schema / properties / fields / properties / machine_settings
        Added value: +{
        +  "description": "The machine's selector positions for this shot, {key: position}; replaces what was stamped, null clears it",
        +  "maxProperties": 16,
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
  5. 1 tool update
    • Changedget_maintenance_guides2 fields changed
      • addedOutput schema / properties / guides / items / properties / steps / items / properties / anchor
        Added value: +{
        +  "type": "string"
        +}
      • changedOutput schema / properties / guides / items / properties / steps / items / required
        Previous value: -[
        -  "n",
        -  "text",
        -  "source"
        -]New value: +[
        +  "n",
        +  "text",
        +  "source",
        +  "anchor"
        +]
  6. 1 tool update
    • Changedlock_recipe2 fields changed
      • addedOutput schema / properties / recipe_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "message"
        -]New value: +[
        +  "status",
        +  "recipe_id",
        +  "message"
        +]
  7. 11 tool updates
    • Changedcompute_age2 fields changed
      • removedOutput schema / properties / days_since_opened / type
        Removed value: -"null"
      • removedOutput schema / properties / grams_remaining / type
        Removed value: -"null"
    • Changedget_dial_state86 fields changed
      • removedOutput schema / properties / active_coffee / properties / acidity / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / arabica_pct / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / aromatics / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / attachments / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / bag_weight_g / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / best_by_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / buy_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / cost / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / currency / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / days_since_opened / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / ean / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / frozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / frozen_note / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / frozen_storage / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / grams_remaining / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / opened_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / qr_code / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / storage / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag / properties / unfrozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bag_weight_g / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / bean_mix / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / best_by_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / body / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / buy_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / co2e_kg / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / cost / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / cupping_points / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / currency / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / days_since_opened / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / decaffeinated / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / ean / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / favourite / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / frozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / frozen_note / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / frozen_storage / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / grams_remaining / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / notes / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / opened_date / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / origin / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / qr_code / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / rating / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / rest_window_days_max / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / rest_window_days_min / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / roast_custom / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / roast_range / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / roasting_type / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / storage / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / sweetness / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / unfrozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / url / type
        Removed value: -"null"
      • removedOutput schema / properties / active_coffee / properties / variety / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / built_in_machine_id / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / catalog_id / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / max_value / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / min_value / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / notes / type
        Removed value: -"null"
      • removedOutput schema / properties / active_grinder / properties / photo / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / built_in_grinder_id / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / connected_device / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / hardness_dh / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / notes / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / photo / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / prep_style / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / prep_type / type
        Removed value: -"null"
      • removedOutput schema / properties / active_machine / properties / tools / type
        Removed value: -"null"
      • removedOutput schema / properties / active_program / type
        Removed value: -"null"
      • removedOutput schema / properties / locked_recipe / properties / grind_reading / type
        Removed value: -"null"
      • removedOutput schema / properties / locked_recipe / properties / program_id / type
        Removed value: -"null"
      • removedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / due_at / type
        Removed value: -"null"
      • removedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / last_at / type
        Removed value: -"null"
      • removedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / muted_until / type
        Removed value: -"null"
      • removedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / overdue_at / type
        Removed value: -"null"
      • removedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / soon_at / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_recommendations / items / properties / based_on_shot / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / beverage_g / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / bloom_time_s / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / ey_pct / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / first_drip_time_s / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / program_id / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / rating / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / tds / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / temperature_c / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / verdict / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / vessel_name / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / vessel_weight_g / type
        Removed value: -"null"
      • removedOutput schema / properties / recent_shots / items / properties / water_id / type
        Removed value: -"null"
    • Changedget_maintenance_guides3 fields changed
      • removedOutput schema / properties / guides / items / properties / machine_alert / properties / can_disable / type
        Removed value: -"null"
      • removedOutput schema / properties / guides / items / properties / machine_alert / properties / depends_on_setting / type
        Removed value: -"null"
      • removedOutput schema / properties / guides / items / properties / machine_alert / properties / threshold / type
        Removed value: -"null"
    • Changedlist_beans41 fields changed
      • removedOutput schema / properties / beans / items / properties / acidity / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / arabica_pct / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / aromatics / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / best_by_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / buy_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / days_since_opened / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / ean / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / frozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / frozen_note / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / frozen_storage / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / opened_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / qr_code / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / storage / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / bags / items / properties / unfrozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / best_by_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / body / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / buy_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / cost / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / cupping_points / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / currency / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / days_since_opened / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / ean / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / frozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / frozen_note / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / frozen_storage / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / opened_date / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origin / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / elevation_m / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / elevation_max_m / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / farm / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / farmer / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / harvest / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / origins / items / properties / processing / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / qr_code / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / rating / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / roasting_type / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / storage / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / sweetness / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / unfrozen_at / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / url / type
        Removed value: -"null"
      • removedOutput schema / properties / beans / items / properties / variety / type
        Removed value: -"null"
    • Changedlist_grinders11 fields changed
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / muted_until / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / guide_id / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / hardness / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / sensitivity / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide / properties / also_covers / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide / properties / duration_minutes / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide / properties / duration_note / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide / properties / machine_alert / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide / properties / market / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / schedule / properties / every / type
        Removed value: -"null"
      • removedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / schedule / properties / muted_until / type
        Removed value: -"null"
    • Changedlist_machines29 fields changed
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / disturbs_note / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / epoch_at / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / epoch_id / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / epoch_task / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / evidence / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / offset_at / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / offset_source / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / offset_unit / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / offset_value / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / calibration / properties / reference_recipe_id / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / catalog / properties / match / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / catalog / properties / via / type
        Removed value: -"null"
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / properties
        Previous value: -[]New value: +{}
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / entries / items / properties / evidence / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / entries / items / properties / note / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / entries / items / properties / offset_at / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / entries / items / properties / offset_source / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / entries / items / properties / offset_value / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / sensitivity / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / disturbs_prompt / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / also_covers / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / duration_note / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / machine_alert / properties / can_disable / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / machine_alert / properties / depends_on_setting / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / machine_alert / properties / threshold / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / market / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / guide / properties / variant / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / maintenance / properties / tasks / items / properties / opened_by / type
        Removed value: -"null"
      • removedOutput schema / properties / machines / items / properties / programs / items / properties / target_time_s / type
        Removed value: -"null"
    • Changedlist_recipes3 fields changed
      • removedOutput schema / properties / recipes / items / properties / grind_reading / properties / label / type
        Removed value: -"null"
      • removedOutput schema / properties / recipes / items / properties / grind_reading / properties / offset_clicks / type
        Removed value: -"null"
      • removedOutput schema / properties / recipes / items / properties / grind_reading / properties / offset_direction / type
        Removed value: -"null"
    • Changedlist_scales11 fields changed
      • removedOutput schema / properties / scales / items / properties / auto_tare / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / auto_timer / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / capacity_g / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / connectivity / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / depth_mm / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / flow_rate_display / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / height_mm / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / notes / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / power / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / water_resistance / type
        Removed value: -"null"
      • removedOutput schema / properties / scales / items / properties / width_mm / type
        Removed value: -"null"
    • Changedlist_shots12 fields changed
      • removedOutput schema / properties / shots / items / properties / beverage_g / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / bloom_time_s / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / ey_pct / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / first_drip_time_s / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / program_id / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / rating / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / tds / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / temperature_c / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / verdict / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / vessel_name / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / vessel_weight_g / type
        Removed value: -"null"
      • removedOutput schema / properties / shots / items / properties / water_id / type
        Removed value: -"null"
    • Changedlist_waters6 fields changed
      • removedOutput schema / properties / waters / items / properties / calcium / type
        Removed value: -"null"
      • removedOutput schema / properties / waters / items / properties / magnesium / type
        Removed value: -"null"
      • removedOutput schema / properties / waters / items / properties / notes / type
        Removed value: -"null"
      • removedOutput schema / properties / waters / items / properties / sodium / type
        Removed value: -"null"
      • removedOutput schema / properties / waters / items / properties / tds / type
        Removed value: -"null"
      • removedOutput schema / properties / waters / items / properties / type / type
        Removed value: -"null"
    • Changedset_maintenance_offset2 fields changed
      • removedOutput schema / properties / entry / properties / details / properties / to / properties / components_spec / type
        Removed value: -"null"
      • removedOutput schema / properties / entry / properties / guide_id / type
        Removed value: -"null"
  8. 5 tool updates
    • Changedget_dial_state5 fields changed
      • addedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / soon
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / maintenance_summary / properties / most_urgent / properties / soon_at
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / maintenance_summary / properties / most_urgent / required
        Previous value: -[
        -  "equipment_kind",
        -  "equipment_id",
        -  "equipment_name",
        -  "task",
        -  "label",
        -  "class",
        -  "state",
        -  "ratio",
        -  "basis",
        -  "every",
        -  "done",
        -  "remaining",
        -  "last_at",
        -  "every_source",
        -  "lower_bound",
        -  "due_at",
        -  "overdue_at",
        -  "muted",
        -  "muted_until"
        -]New value: +[
        +  "equipment_kind",
        +  "equipment_id",
        +  "equipment_name",
        +  "task",
        +  "label",
        +  "class",
        +  "state",
        +  "ratio",
        +  "basis",
        +  "every",
        +  "done",
        +  "remaining",
        +  "last_at",
        +  "every_source",
        +  "lower_bound",
        +  "soon",
        +  "soon_at",
        +  "due_at",
        +  "overdue_at",
        +  "muted",
        +  "muted_until"
        +]
      • addedOutput schema / properties / maintenance_summary / properties / soon_count
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / maintenance_summary / required
        Previous value: -[
        -  "computed_at",
        -  "catalog_version",
        -  "most_urgent",
        -  "due_count",
        -  "overdue_count",
        -  "calibration_state"
        -]New value: +[
        +  "computed_at",
        +  "catalog_version",
        +  "most_urgent",
        +  "due_count",
        +  "overdue_count",
        +  "soon_count",
        +  "calibration_state"
        +]
    • Changedlist_grinders3 fields changed
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / soon
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / soon_at
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / required
        Previous value: -[
        -  "state",
        -  "ratio",
        -  "basis",
        -  "every",
        -  "done",
        -  "remaining",
        -  "lower_bound",
        -  "due_at",
        -  "overdue_at",
        -  "last_at",
        -  "every_source",
        -  "muted",
        -  "muted_until"
        -]New value: +[
        +  "state",
        +  "ratio",
        +  "basis",
        +  "every",
        +  "done",
        +  "remaining",
        +  "lower_bound",
        +  "soon",
        +  "soon_at",
        +  "due_at",
        +  "overdue_at",
        +  "last_at",
        +  "every_source",
        +  "muted",
        +  "muted_until"
        +]
    • Changedlist_machines3 fields changed
      • addedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / properties / soon
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / properties / soon_at
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / machines / items / properties / maintenance / properties / due / additionalProperties / required
        Previous value: -[
        -  "state",
        -  "ratio",
        -  "basis",
        -  "every",
        -  "done",
        -  "remaining",
        -  "lower_bound",
        -  "due_at",
        -  "overdue_at",
        -  "last_at",
        -  "every_source",
        -  "muted",
        -  "muted_until"
        -]New value: +[
        +  "state",
        +  "ratio",
        +  "basis",
        +  "every",
        +  "done",
        +  "remaining",
        +  "lower_bound",
        +  "soon",
        +  "soon_at",
        +  "due_at",
        +  "overdue_at",
        +  "last_at",
        +  "every_source",
        +  "muted",
        +  "muted_until"
        +]
    • Changedlist_maintenance_due3 fields changed
      • addedOutput schema / properties / items / items / properties / soon
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / items / items / properties / soon_at
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / items / items / required
        Previous value: -[
        -  "equipment_kind",
        -  "equipment_id",
        -  "equipment_name",
        -  "task",
        -  "label",
        -  "class",
        -  "state",
        -  "ratio",
        -  "basis",
        -  "every",
        -  "done",
        -  "remaining",
        -  "last_at",
        -  "every_source",
        -  "lower_bound",
        -  "due_at",
        -  "overdue_at",
        -  "muted",
        -  "muted_until"
        -]New value: +[
        +  "equipment_kind",
        +  "equipment_id",
        +  "equipment_name",
        +  "task",
        +  "label",
        +  "class",
        +  "state",
        +  "ratio",
        +  "basis",
        +  "every",
        +  "done",
        +  "remaining",
        +  "last_at",
        +  "every_source",
        +  "lower_bound",
        +  "soon",
        +  "soon_at",
        +  "due_at",
        +  "overdue_at",
        +  "muted",
        +  "muted_until"
        +]
    • Changedset_maintenance_schedule2 fields changed
      • addedOutput schema / properties / due / properties / soon
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / due / properties / soon_at
        Added value: +{
        +  "type": "string"
        +}
  9. 3 tool updates
    • Changedregister_coffee2 fields changed
      • addedOutput schema / properties / active
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "bean_id",
        -  "bag_id",
        -  "message"
        -]New value: +[
        +  "status",
        +  "bean_id",
        +  "bag_id",
        +  "active",
        +  "message"
        +]
    • Changedregister_grinder2 fields changed
      • addedOutput schema / properties / active
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "grinder_id",
        -  "message"
        -]New value: +[
        +  "status",
        +  "grinder_id",
        +  "active",
        +  "message"
        +]
    • Changedregister_machine2 fields changed
      • addedOutput schema / properties / active
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "machine_id",
        -  "message"
        -]New value: +[
        +  "status",
        +  "machine_id",
        +  "active",
        +  "message"
        +]
  10. 12 tool updates
    • Changedget_dial_state7 fields changed
      • addedOutput schema / properties / active_grinder / properties / catalog_id
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / active_grinder / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "motor_type",
        -  "burr_type",
        -  "setting_scheme",
        -  "direction_convention",
        -  "nominal_step",
        -  "components_spec",
        -  "min_value",
        -  "max_value",
        -  "microns_per_step",
        -  "notes",
        -  "is_active",
        -  "archived",
        -  "photo",
        -  "setting_label",
        -  "setting_components",
        -  "position_value",
        -  "source",
        -  "verified_at",
        -  "built_in_machine_id"
        -]New value: +[
        +  "id",
        +  "name",
        +  "motor_type",
        +  "burr_type",
        +  "setting_scheme",
        +  "direction_convention",
        +  "nominal_step",
        +  "components_spec",
        +  "min_value",
        +  "max_value",
        +  "microns_per_step",
        +  "notes",
        +  "is_active",
        +  "archived",
        +  "photo",
        +  "catalog_id",
        +  "setting_label",
        +  "setting_components",
        +  "position_value",
        +  "source",
        +  "verified_at",
        +  "built_in_machine_id"
        +]
      • addedOutput schema / properties / active_machine / properties / catalog_id
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / active_machine / properties / hardness_dh
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / active_machine / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "control_type",
        -  "basket_size_grams",
        -  "boiler_type",
        -  "notes",
        -  "is_active",
        -  "prep_type",
        -  "prep_style",
        -  "archived",
        -  "photo",
        -  "tools",
        -  "connected_device",
        -  "built_in_grinder_id"
        -]New value: +[
        +  "id",
        +  "name",
        +  "control_type",
        +  "basket_size_grams",
        +  "boiler_type",
        +  "notes",
        +  "is_active",
        +  "prep_type",
        +  "prep_style",
        +  "archived",
        +  "photo",
        +  "tools",
        +  "connected_device",
        +  "built_in_grinder_id",
        +  "catalog_id",
        +  "hardness_dh"
        +]
      • addedOutput schema / properties / maintenance_summary
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "calibration_state": {
        +      "type": "string"
        +    },
        +    "catalog_version": {
        +      "type": "string"
        +    },
        +    "computed_at": {
        +      "type": "string"
        +    },
        +    "due_count": {
        +      "type": "integer"
        +    },
        +    "most_urgent": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "basis": {
        +          "type": "string"
        +        },
        +        "class": {
        +          "type": "string"
        +        },
        +        "done": {
        +          "type": "integer"
        +        },
        +        "due_at": {
        +          "type": "null"
        +        },
        +        "equipment_id": {
        +          "type": "integer"
        +        },
        +        "equipment_kind": {
        +          "type": "string"
        +        },
        +        "equipment_name": {
        +          "type": "string"
        +        },
        +        "every": {
        +          "type": "integer"
        +        },
        +        "every_source": {
        +          "type": "string"
        +        },
        +        "label": {
        +          "type": "string"
        +        },
        +        "last_at": {
        +          "type": "null"
        +        },
        +        "lower_bound": {
        +          "type": "boolean"
        +        },
        +        "muted": {
        +          "type": "boolean"
        +        },
        +        "muted_until": {
        +          "type": "null"
        +        },
        +        "overdue_at": {
        +          "type": "null"
        +        },
        +        "ratio": {
        +          "type": "number"
        +        },
        +        "remaining": {
        +          "type": "integer"
        +        },
        +        "state": {
        +          "type": "string"
        +        },
        +        "task": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "equipment_kind",
        +        "equipment_id",
        +        "equipment_name",
        +        "task",
        +        "label",
        +        "class",
        +        "state",
        +        "ratio",
        +        "basis",
        +        "every",
        +        "done",
        +        "remaining",
        +        "last_at",
        +        "every_source",
        +        "lower_bound",
        +        "due_at",
        +        "overdue_at",
        +        "muted",
        +        "muted_until"
        +      ],
        +      "type": "object"
        +    },
        +    "overdue_count": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "computed_at",
        +    "catalog_version",
        +    "most_urgent",
        +    "due_count",
        +    "overdue_count",
        +    "calibration_state"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "active_coffee",
        -  "active_grinder",
        -  "active_machine",
        -  "active_program",
        -  "recent_shots",
        -  "recent_recommendations",
        -  "locked_recipe"
        -]New value: +[
        +  "active_coffee",
        +  "active_grinder",
        +  "active_machine",
        +  "active_program",
        +  "recent_shots",
        +  "recent_recommendations",
        +  "locked_recipe",
        +  "maintenance_summary"
        +]
    • Addedget_maintenance_guides
    • Changedlist_grinders45 fields changed
      • addedInput schema / properties / maintenance
        Added value: +{
        +  "description": "How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words — quote, source, alternates, the interval as written.",
        +  "enum": [
        +    "compact",
        +    "full"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / grinders / items / properties / built_in_machine_id / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedOutput schema / properties / grinders / items / properties / catalog_id
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / disturbs_note / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_at / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_id / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_task / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_at / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_source / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_value / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / catalog
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "brand": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "declined": {
        +      "type": "boolean"
        +    },
        +    "id": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "match": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "brand": {
        +          "type": "string"
        +        },
        +        "by": {
        +          "type": "string"
        +        },
        +        "id": {
        +          "type": "string"
        +        },
        +        "model": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [],
        +      "type": [
        +        "null",
        +        "object"
        +      ]
        +    },
        +    "model": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    },
        +    "via": {
        +      "type": [
        +        "string",
        +        "null"
        +      ]
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "brand",
        +    "model",
        +    "via",
        +    "match",
        +    "declined"
        +  ],
        +  "type": "object"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / catalog_version
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / computed_at
        Added value: +{
        +  "type": "string"
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / basis / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / done / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / due_at
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / every / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / every_source
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / lower_bound
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / muted
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / muted_until
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / overdue_at
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / ratio / type
        Previous value: -[
        -  "number",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / remaining / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / required
        Previous value: -[
        -  "state",
        -  "ratio",
        -  "basis",
        -  "every",
        -  "done",
        -  "remaining",
        -  "last_at"
        -]New value: +[
        +  "state",
        +  "ratio",
        +  "basis",
        +  "every",
        +  "done",
        +  "remaining",
        +  "lower_bound",
        +  "due_at",
        +  "overdue_at",
        +  "last_at",
        +  "every_source",
        +  "muted",
        +  "muted_until"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / guide_id
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / task_label
        Added value: +{
        +  "type": "string"
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / required
        Previous value: -[
        -  "id",
        -  "equipment_kind",
        -  "equipment_id",
        -  "task",
        -  "performed_at",
        -  "disturbs_zero",
        -  "note",
        -  "offset_value",
        -  "offset_source",
        -  "offset_at",
        -  "evidence",
        -  "details",
        -  "details_version",
        -  "created_at",
        -  "details_known"
        -]New value: +[
        +  "id",
        +  "equipment_kind",
        +  "equipment_id",
        +  "task",
        +  "performed_at",
        +  "disturbs_zero",
        +  "note",
        +  "offset_value",
        +  "offset_source",
        +  "offset_at",
        +  "evidence",
        +  "details",
        +  "details_version",
        +  "created_at",
        +  "guide_id",
        +  "details_known",
        +  "task_label"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries_total
        Added value: +{
        +  "type": "integer"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / hardness
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / class
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / details_fields / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "hint": {
        +      "type": "string"
        +    },
        +    "key": {
        +      "type": "string"
        +    },
        +    "label": {
        +      "type": "string"
        +    },
        +    "required": {
        +      "type": "boolean"
        +    },
        +    "type": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "key",
        +    "type",
        +    "label",
        +    "hint",
        +    "required"
        +  ],
        +  "type": "object"
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / disturbs_prompt
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / every / type
        Previous value: -[
        -  "object",
        -  "null"
        -]New value: +[
        +  "null",
        +  "object"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / every_source
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "days": {
        +      "type": "string"
        +    },
        +    "grams": {
        +      "type": "string"
        +    },
        +    "shots": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [],
        +  "type": [
        +    "null",
        +    "object"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / guide
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "also_covers": {
        +      "type": "null"
        +    },
        +    "basis": {
        +      "type": "string"
        +    },
        +    "duration_minutes": {
        +      "type": "null"
        +    },
        +    "duration_note": {
        +      "type": "null"
        +    },
        +    "id": {
        +      "type": "string"
        +    },
        +    "machine_alert": {
        +      "type": "null"
        +    },
        +    "market": {
        +      "type": "null"
        +    },
        +    "scope": {
        +      "type": "string"
        +    },
        +    "steps": {
        +      "type": "integer"
        +    },
        +    "title": {
        +      "type": "string"
        +    },
        +    "variant": {
        +      "type": "string"
        +    },
        +    "warnings": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [],
        +  "type": [
        +    "object",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / schedule
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "enabled": {
        +      "type": "boolean"
        +    },
        +    "every": {
        +      "type": "null"
        +    },
        +    "muted_until": {
        +      "type": "null"
        +    }
        +  },
        +  "required": [],
        +  "type": [
        +    "null",
        +    "object"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / status
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / trigger
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / required
        Previous value: -[
        -  "value",
        -  "kind",
        -  "label",
        -  "sub",
        -  "disturbs_zero",
        -  "every",
        -  "why",
        -  "disturbs_note",
        -  "offset_unit",
        -  "offset_integral",
        -  "scheduled",
        -  "opened_by",
        -  "details_version",
        -  "details_fields"
        -]New value: +[
        +  "value",
        +  "kind",
        +  "label",
        +  "sub",
        +  "disturbs_zero",
        +  "every",
        +  "why",
        +  "disturbs_note",
        +  "offset_unit",
        +  "offset_integral",
        +  "scheduled",
        +  "opened_by",
        +  "details_version",
        +  "details_fields",
        +  "class",
        +  "status",
        +  "trigger",
        +  "every_source",
        +  "disturbs_prompt",
        +  "guide",
        +  "schedule"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / required
        Previous value: -[
        -  "tasks",
        -  "due",
        -  "calibration",
        -  "sensitivity",
        -  "entries"
        -]New value: +[
        +  "tasks",
        +  "due",
        +  "calibration",
        +  "sensitivity",
        +  "entries",
        +  "entries_total",
        +  "catalog_version",
        +  "computed_at",
        +  "catalog",
        +  "hardness"
        +]
      • changedOutput schema / properties / grinders / items / properties / microns_per_step / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / nominal_step / type
        Previous value: -[
        -  "number",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / grinders / items / properties / notes / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "motor_type",
        -  "burr_type",
        -  "setting_scheme",
        -  "direction_convention",
        -  "nominal_step",
        -  "components_spec",
        -  "min_value",
        -  "max_value",
        -  "microns_per_step",
        -  "notes",
        -  "archived",
        -  "setting_label",
        -  "setting_components",
        -  "position_value",
        -  "source",
        -  "verified_at",
        -  "built_in_machine_id",
        -  "maintenance"
        -]New value: +[
        +  "id",
        +  "name",
        +  "motor_type",
        +  "burr_type",
        +  "setting_scheme",
        +  "direction_convention",
        +  "nominal_step",
        +  "components_spec",
        +  "min_value",
        +  "max_value",
        +  "microns_per_step",
        +  "notes",
        +  "archived",
        +  "catalog_id",
        +  "setting_label",
        +  "setting_components",
        +  "position_value",
        +  "source",
        +  "verified_at",
        +  "built_in_machine_id",
        +  "maintenance"
        +]
    • Changedlist_machines6 fields changed
      • addedInput schema / properties / maintenance
        Added value: +{
        +  "description": "How much of each row's maintenance block to include. compact (default): every task with its class, due state, schedule and guide. full: also the maker's own words — quote, source, alternates, the interval as written.",
        +  "enum": [
        +    "compact",
        +    "full"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / machines / items / properties / built_in_grinder_id / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "integer"
        +]
      • addedOutput schema / properties / machines / items / properties / catalog_id
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • addedOutput schema / properties / machines / items / properties / hardness_dh
        Added value: +{
        +  "type": [
        +    "null",
        +    "number"
        +  ]
        +}
      • addedOutput schema / properties / machines / items / properties / maintenance
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "calibration": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "disturbs_note": {
        +          "type": "null"
        +        },
        +        "epoch_at": {
        +          "type": "null"
        +        },
        +        "epoch_id": {
        +          "type": "null"
        +        },
        +        "epoch_task": {
        +          "type": "null"
        +        },
        +        "evidence": {
        +          "type": "null"
        +        },
        +        "offset_at": {
        +          "type": "null"
        +        },
        +        "offset_source": {
        +          "type": "null"
        +        },
        +        "offset_unit": {
        +          "type": "null"
        +        },
        +        "offset_value": {
        +          "type": "null"
        +        },
        +        "reference_recipe_id": {
        +          "type": "null"
        +        },
        +        "stale_recipe_count": {
        +          "type": "integer"
        +        },
        +        "state": {
        +          "type": "string"
        +        },
        +        "untranslatable_recipe_count": {
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "stale_recipe_count",
        +        "untranslatable_recipe_count",
        +        "reference_recipe_id",
        +        "state",
        +        "epoch_id",
        +        "epoch_task",
        +        "disturbs_note",
        +        "epoch_at",
        +        "offset_value",
        +        "offset_unit",
        +        "offset_source",
        +        "offset_at",
        +        "evidence"
        +      ],
        +      "type": "object"
        +    },
        +    "catalog": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "brand": {
        +          "type": [
        +            "null",
        +            "string"
        +          ]
        +        },
        +        "declined": {
        +          "type": "boolean"
        +        },
        +        "id": {
        +          "type": [
        +            "null",
        +            "string"
        +          ]
        +        },
        +        "match": {
        +          "type": "null"
        +        },
        +        "model": {
        +          "type": [
        +            "null",
        +            "string"
        +          ]
        +        },
        +        "via": {
        +          "type": "null"
        +        }
        +      },
        +      "required": [
        +        "id",
        +        "brand",
        +        "model",
        +        "via",
        +        "match",
        +        "declined"
        +      ],
        +      "type": "object"
        +    },
        +    "catalog_version": {
        +      "type": "string"
        +    },
        +    "computed_at": {
        +      "type": "string"
        +    },
        +    "due": {
        +      "additionalProperties": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "basis": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "done": {
        +            "type": [
        +              "null",
        +              "integer"
        +            ]
        +          },
        +          "due_at": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "every": {
        +            "type": [
        +              "null",
        +              "integer"
        +            ]
        +          },
        +          "every_source": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "last_at": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "lower_bound": {
        +            "type": "boolean"
        +          },
        +          "muted": {
        +            "type": "boolean"
        +          },
        +          "muted_until": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "overdue_at": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "ratio": {
        +            "type": [
        +              "integer",
        +              "number"
        +            ]
        +          },
        +          "remaining": {
        +            "type": [
        +              "null",
        +              "integer"
        +            ]
        +          },
        +          "state": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "state",
        +          "ratio",
        +          "basis",
        +          "every",
        +          "done",
        +          "remaining",
        +          "lower_bound",
        +          "due_at",
        +          "overdue_at",
        +          "last_at",
        +          "every_source",
        +          "muted",
        +          "muted_until"
        +        ],
        +        "type": "object"
        +      },
        +      "properties": [],
        +      "required": [],
        +      "type": [
        +        "null",
        +        "object"
        +      ]
        +    },
        +    "entries": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "created_at": {
        +            "type": "string"
        +          },
        +          "details": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "filter_id": {
        +                "type": "string"
        +              },
        +              "label": {
        +                "type": "string"
        +              },
        +              "product": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [],
        +            "type": "object"
        +          },
        +          "details_known": {
        +            "type": "boolean"
        +          },
        +          "details_version": {
        +            "type": "integer"
        +          },
        +          "disturbs_zero": {
        +            "type": "integer"
        +          },
        +          "equipment_id": {
        +            "type": "integer"
        +          },
        +          "equipment_kind": {
        +            "type": "string"
        +          },
        +          "evidence": {
        +            "type": "null"
        +          },
        +          "guide_id": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "id": {
        +            "type": "integer"
        +          },
        +          "note": {
        +            "type": "null"
        +          },
        +          "offset_at": {
        +            "type": "null"
        +          },
        +          "offset_source": {
        +            "type": "null"
        +          },
        +          "offset_value": {
        +            "type": "null"
        +          },
        +          "performed_at": {
        +            "type": "string"
        +          },
        +          "task": {
        +            "type": "string"
        +          },
        +          "task_label": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "equipment_kind",
        +          "equipment_id",
        +          "task",
        +          "performed_at",
        +          "disturbs_zero",
        +          "note",
        +          "offset_value",
        +          "offset_source",
        +          "offset_at",
        +          "evidence",
        +          "details",
        +          "details_version",
        +          "created_at",
        +          "guide_id",
        +          "details_known",
        +          "task_label"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "entries_total": {
        +      "type": "integer"
        +    },
        +    "hardness": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "dh": {
        +          "type": [
        +            "null",
        +            "number"
        +          ]
        +        },
        +        "dkh": {
        +          "type": [
        +            "null",
        +            "number"
        +          ]
        +        },
        +        "dkh_source": {
        +          "type": [
        +            "null",
        +            "string"
        +          ]
        +        },
        +        "source": {
        +          "type": [
        +            "null",
        +            "string"
        +          ]
        +        }
        +      },
        +      "required": [
        +        "dh",
        +        "dkh",
        +        "source",
        +        "dkh_source"
        +      ],
        +      "type": "object"
        +    },
        +    "sensitivity": {
        +      "type": "null"
        +    },
        +    "tasks": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "class": {
        +            "type": "string"
        +          },
        +          "details_fields": {
        +            "items": {
        +              "additionalProperties": true,
        +              "properties": {
        +                "hint": {
        +                  "type": [
        +                    "string",
        +                    "null"
        +                  ]
        +                },
        +                "key": {
        +                  "type": "string"
        +                },
        +                "label": {
        +                  "type": "string"
        +                },
        +                "options": {
        +                  "items": {
        +                    "additionalProperties": true,
        +                    "properties": {
        +                      "id": {
        +                        "type": "string"
        +                      },
        +                      "label": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "id",
        +                      "label"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "type": "array"
        +                },
        +                "required": {
        +                  "type": "boolean"
        +                },
        +                "source": {
        +                  "type": "string"
        +                },
        +                "type": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "key",
        +                "type",
        +                "label",
        +                "hint",
        +                "required"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          },
        +          "details_version": {
        +            "type": "integer"
        +          },
        +          "disturbs_note": {
        +            "type": "string"
        +          },
        +          "disturbs_prompt": {
        +            "type": "null"
        +          },
        +          "disturbs_zero": {
        +            "type": "boolean"
        +          },
        +          "every": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "days": {
        +                "type": "integer"
        +              },
        +              "litres": {
        +                "type": [
        +                  "integer",
        +                  "number"
        +                ]
        +              },
        +              "shots": {
        +                "type": "integer"
        +              }
        +            },
        +            "required": [],
        +            "type": [
        +              "null",
        +              "object"
        +            ]
        +          },
        +          "every_source": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "days": {
        +                "type": "string"
        +              },
        +              "litres": {
        +                "type": "string"
        +              },
        +              "shots": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [],
        +            "type": [
        +              "null",
        +              "object"
        +            ]
        +          },
        +          "guide": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "also_covers": {
        +                "type": "null"
        +              },
        +              "basis": {
        +                "type": "string"
        +              },
        +              "duration_minutes": {
        +                "type": [
        +                  "integer",
        +                  "null"
        +                ]
        +              },
        +              "duration_note": {
        +                "type": "null"
        +              },
        +              "id": {
        +                "type": "string"
        +              },
        +              "machine_alert": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "can_disable": {
        +                    "type": "null"
        +                  },
        +                  "clears": {
        +                    "type": [
        +                      "null",
        +                      "string"
        +                    ]
        +                  },
        +                  "counts": {
        +                    "type": "string"
        +                  },
        +                  "depends_on_setting": {
        +                    "type": "null"
        +                  },
        +                  "indicator": {
        +                    "type": "string"
        +                  },
        +                  "shown_as": {
        +                    "type": "string"
        +                  },
        +                  "source": {
        +                    "type": "string"
        +                  },
        +                  "threshold": {
        +                    "type": "null"
        +                  }
        +                },
        +                "required": [],
        +                "type": [
        +                  "object",
        +                  "null"
        +                ]
        +              },
        +              "market": {
        +                "type": "null"
        +              },
        +              "scope": {
        +                "type": "string"
        +              },
        +              "steps": {
        +                "type": "integer"
        +              },
        +              "title": {
        +                "type": "string"
        +              },
        +              "variant": {
        +                "type": "null"
        +              },
        +              "warnings": {
        +                "type": "integer"
        +              }
        +            },
        +            "required": [],
        +            "type": [
        +              "null",
        +              "object"
        +            ]
        +          },
        +          "kind": {
        +            "type": "string"
        +          },
        +          "label": {
        +            "type": "string"
        +          },
        +          "offset_integral": {
        +            "type": "boolean"
        +          },
        +          "offset_unit": {
        +            "type": "string"
        +          },
        +          "opened_by": {
        +            "type": "null"
        +          },
        +          "schedule": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "enabled": {
        +                "type": "boolean"
        +              },
        +              "every": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "days": {
        +                    "type": "integer"
        +                  },
        +                  "litres": {
        +                    "type": "number"
        +                  }
        +                },
        +                "required": [
        +                  "days",
        +                  "litres"
        +                ],
        +                "type": "object"
        +              },
        +              "muted_until": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [],
        +            "type": [
        +              "null",
        +              "object"
        +            ]
        +          },
        +          "scheduled": {
        +            "type": "boolean"
        +          },
        +          "status": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "sub": {
        +            "type": "string"
        +          },
        +          "trigger": {
        +            "type": [
        +              "null",
        +              "string"
        +            ]
        +          },
        +          "value": {
        +            "type": "string"
        +          },
        +          "why": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "value",
        +          "kind",
        +          "label",
        +          "sub",
        +          "disturbs_zero",
        +          "every",
        +          "why",
        +          "disturbs_note",
        +          "offset_unit",
        +          "offset_integral",
        +          "scheduled",
        +          "opened_by",
        +          "details_version",
        +          "details_fields",
        +          "class",
        +          "status",
        +          "trigger",
        +          "every_source",
        +          "disturbs_prompt",
        +          "guide",
        +          "schedule"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "tasks",
        +    "due",
        +    "calibration",
        +    "sensitivity",
        +    "entries",
        +    "entries_total",
        +    "catalog_version",
        +    "computed_at",
        +    "catalog",
        +    "hardness"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / machines / items / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "control_type",
        -  "basket_size_grams",
        -  "boiler_type",
        -  "notes",
        -  "prep_type",
        -  "prep_style",
        -  "archived",
        -  "built_in_grinder_id",
        -  "tools_list",
        -  "programs"
        -]New value: +[
        +  "id",
        +  "name",
        +  "control_type",
        +  "basket_size_grams",
        +  "boiler_type",
        +  "notes",
        +  "prep_type",
        +  "prep_style",
        +  "archived",
        +  "built_in_grinder_id",
        +  "catalog_id",
        +  "hardness_dh",
        +  "tools_list",
        +  "programs",
        +  "maintenance"
        +]
    • Addedlist_maintenance_due
    • Changedlog_maintenance12 fields changed
      • changedInput schema / properties / details / description
        Previous value: -"Fields specific to this task. Which ones a task takes is published as details_fields in the task catalog on list_grinders; a burr clean takes none"New value: +"Fields specific to this task. Which ones a task takes is published as details_fields on each task; a burr clean takes none"
      • changedInput schema / properties / equipment_kind / enum
        Previous value: -[
        -  "grinder"
        -]New value: +[
        +  "grinder",
        +  "machine"
        +]
      • addedInput schema / properties / guide_id
        Added value: +{
        +  "description": "Optional — the id of the maintenance guide that was followed. Must be a guide for this task",
        +  "type": "string"
        +}
      • changedInput schema / properties / task / enum
        Previous value: -[
        -  "burr_clean"
        -]New value: +[
        +  "burr_clean",
        +  "backflush_water",
        +  "backflush_detergent",
        +  "descale",
        +  "group_gasket_replace",
        +  "shower_screen_clean",
        +  "shower_screen_replace",
        +  "group_head_wipe",
        +  "basket_portafilter_soak",
        +  "steam_wand_purge",
        +  "steam_wand_soak",
        +  "drip_tray_clean",
        +  "water_tank_clean",
        +  "water_filter_replace",
        +  "boiler_water_change",
        +  "lever_piston_seal_replace",
        +  "lever_lubricate",
        +  "milk_system_clean",
        +  "professional_service",
        +  "builtin_grinder_burr_clean",
        +  "builtin_grinder_burr_replace",
        +  "grinder_quick_clean",
        +  "grinder_cleaning_tablets",
        +  "burr_replace",
        +  "hopper_clean",
        +  "grounds_cup_clean",
        +  "bearing_lubricate",
        +  "gasket_seal_replace",
        +  "idle_rinse",
        +  "water_wash",
        +  "dishwasher",
        +  "filter_prime",
        +  "softener_regenerate",
        +  "other"
        +]
      • addedOutput schema / properties / deduped
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / entry / properties / details / additionalProperties
        Added value: +true
      • addedOutput schema / properties / entry / properties / details / properties
        Added value: +{
        +  "filter_id": {
        +    "type": "string"
        +  },
        +  "label": {
        +    "type": "string"
        +  },
        +  "product": {
        +    "type": "string"
        +  }
        +}
      • addedOutput schema / properties / entry / properties / details / required
        Added value: +[]
      • changedOutput schema / properties / entry / properties / details / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "object"
        +]
      • addedOutput schema / properties / entry / properties / guide_id
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • changedOutput schema / properties / entry / required
        Previous value: -[
        -  "id",
        -  "equipment_kind",
        -  "equipment_id",
        -  "task",
        -  "performed_at",
        -  "disturbs_zero",
        -  "note",
        -  "offset_value",
        -  "offset_source",
        -  "offset_at",
        -  "evidence",
        -  "details",
        -  "details_version",
        -  "created_at",
        -  "details_known"
        -]New value: +[
        +  "id",
        +  "equipment_kind",
        +  "equipment_id",
        +  "task",
        +  "performed_at",
        +  "disturbs_zero",
        +  "note",
        +  "offset_value",
        +  "offset_source",
        +  "offset_at",
        +  "evidence",
        +  "details",
        +  "details_version",
        +  "created_at",
        +  "guide_id",
        +  "details_known"
        +]
      • changedOutput schema / required
        Previous value: -[
        -  "entry_id",
        -  "entry",
        -  "message"
        -]New value: +[
        +  "entry_id",
        +  "entry",
        +  "deduped",
        +  "message"
        +]
    • Changedregister_grinder1 field changed
      • addedInput schema / properties / catalog_id
        Added value: +{
        +  "description": "Optional — the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog.",
        +  "type": "string"
        +}
    • Changedregister_machine2 fields changed
      • addedInput schema / properties / catalog_id
        Added value: +{
        +  "description": "Optional — the maintenance catalog model this is (the `id` of a catalog row, e.g. rancilio-silvia), when it was registered from the catalog. Its maintenance then comes from that model's researched record. \"none\" says it is not in the catalog.",
        +  "type": "string"
        +}
      • addedInput schema / properties / hardness_dh
        Added value: +{
        +  "description": "Optional — the TOTAL hardness (GH) of the water this machine runs on, in °dH (German degrees; ppm as CaCO3 ÷ 17.85), as a test strip reports it. Picks the band of a maker's hardness table for descaling and filter life",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
    • Changedset_maintenance_offset2 fields changed
      • addedOutput schema / properties / entry / properties / guide_id
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / entry / required
        Previous value: -[
        -  "id",
        -  "equipment_kind",
        -  "equipment_id",
        -  "task",
        -  "performed_at",
        -  "disturbs_zero",
        -  "note",
        -  "offset_value",
        -  "offset_source",
        -  "offset_at",
        -  "evidence",
        -  "details",
        -  "details_version",
        -  "created_at",
        -  "details_known"
        -]New value: +[
        +  "id",
        +  "equipment_kind",
        +  "equipment_id",
        +  "task",
        +  "performed_at",
        +  "disturbs_zero",
        +  "note",
        +  "offset_value",
        +  "offset_source",
        +  "offset_at",
        +  "evidence",
        +  "details",
        +  "details_version",
        +  "created_at",
        +  "guide_id",
        +  "details_known"
        +]
    • Addedset_maintenance_schedule
    • Changedupdate_grinder1 field changed
      • addedInput schema / properties / fields / properties / catalog_id
        Added value: +{
        +  "description": "The maintenance catalog model this grinder is; \"none\" if it is not listed; null to unbind. list_grinders proposes one from the name under maintenance.catalog.match",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
    • Changedupdate_machine2 fields changed
      • addedInput schema / properties / fields / properties / catalog_id
        Added value: +{
        +  "description": "The maintenance catalog model this machine is; \"none\" if it is not listed; null to unbind. list_machines proposes one from the name under maintenance.catalog.match",
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / fields / properties / hardness_dh
        Added value: +{
        +  "description": "The total hardness (GH) of the water this machine runs on, in °dH; null to fall back to the water of its last shot",
        +  "type": [
        +    "number",
        +    "null"
        +  ]
        +}
  11. 10 tool updates
    • Changedlist_grinders33 fields changed
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / disturbs_note
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_at / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_id / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_task
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_at / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_source / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_value / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / untranslatable_recipe_count
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / required
        Previous value: -[
        -  "stale_recipe_count",
        -  "reference_recipe_id",
        -  "state",
        -  "epoch_id",
        -  "epoch_at",
        -  "offset_value",
        -  "offset_unit",
        -  "offset_source",
        -  "offset_at",
        -  "evidence"
        -]New value: +[
        +  "stale_recipe_count",
        +  "untranslatable_recipe_count",
        +  "reference_recipe_id",
        +  "state",
        +  "epoch_id",
        +  "epoch_task",
        +  "disturbs_note",
        +  "epoch_at",
        +  "offset_value",
        +  "offset_unit",
        +  "offset_source",
        +  "offset_at",
        +  "evidence"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / basis / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / done / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / every / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / ratio / type
        Previous value: -"integer"New value: +[
        +  "number",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / remaining / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / details / additionalProperties
        Added value: +true
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / details / properties
        Added value: +{
        +  "from": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "components_spec": {
        +        "type": [
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "nominal_step": {
        +        "type": [
        +          "number",
        +          "integer"
        +        ]
        +      },
        +      "setting_scheme": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "setting_scheme",
        +      "nominal_step",
        +      "components_spec"
        +    ],
        +    "type": "object"
        +  },
        +  "to": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "components_spec": {
        +        "type": [
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "nominal_step": {
        +        "type": [
        +          "number",
        +          "integer"
        +        ]
        +      },
        +      "setting_scheme": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "setting_scheme",
        +      "nominal_step",
        +      "components_spec"
        +    ],
        +    "type": "object"
        +  }
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / details / required
        Added value: +[]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / details / type
        Previous value: -"null"New value: +[
        +  "object",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / evidence / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / note / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_at / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_source / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_value / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / every / required
        Previous value: -[
        -  "days",
        -  "shots",
        -  "grams"
        -]New value: +[]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / every / type
        Previous value: -"object"New value: +[
        +  "object",
        +  "null"
        +]
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / opened_by
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / properties / scheduled
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / tasks / items / required
        Previous value: -[
        -  "value",
        -  "kind",
        -  "label",
        -  "sub",
        -  "disturbs_zero",
        -  "every",
        -  "why",
        -  "disturbs_note",
        -  "offset_unit",
        -  "offset_integral",
        -  "details_version",
        -  "details_fields"
        -]New value: +[
        +  "value",
        +  "kind",
        +  "label",
        +  "sub",
        +  "disturbs_zero",
        +  "every",
        +  "why",
        +  "disturbs_note",
        +  "offset_unit",
        +  "offset_integral",
        +  "scheduled",
        +  "opened_by",
        +  "details_version",
        +  "details_fields"
        +]
      • changedOutput schema / properties / grinders / items / properties / max_value / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / microns_per_step / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / min_value / type
        Previous value: -[
        -  "integer",
        -  "null"
        -]New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / nominal_step / type
        Previous value: -"integer"New value: +[
        +  "number",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / notes / type
        Previous value: -"null"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedlist_machines2 fields changed
      • changedOutput schema / properties / machines / items / properties / notes / type
        Previous value: -"null"New value: +[
        +  "string",
        +  "null"
        +]
      • addedOutput schema / properties / machines / items / properties / tools_list / items
        Added value: +{
        +  "type": "string"
        +}
    • Changedlist_recipes2 fields changed
      • addedOutput schema / properties / recipes / items / properties / grind_reading / properties / epoch_tasks
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / recipes / items / properties / grind_reading / properties / untranslatable
        Added value: +{
        +  "type": "boolean"
        +}
    • Changedlist_shots1 field changed
      • changedOutput schema / properties / shots / items / properties / flavor_notes / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedlog_maintenance2 fields changed
      • addedOutput schema / properties / entry / properties / details_known
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / entry / required
        Previous value: -[
        -  "id",
        -  "equipment_kind",
        -  "equipment_id",
        -  "task",
        -  "performed_at",
        -  "disturbs_zero",
        -  "note",
        -  "offset_value",
        -  "offset_source",
        -  "offset_at",
        -  "evidence",
        -  "details",
        -  "details_version",
        -  "created_at"
        -]New value: +[
        +  "id",
        +  "equipment_kind",
        +  "equipment_id",
        +  "task",
        +  "performed_at",
        +  "disturbs_zero",
        +  "note",
        +  "offset_value",
        +  "offset_source",
        +  "offset_at",
        +  "evidence",
        +  "details",
        +  "details_version",
        +  "created_at",
        +  "details_known"
        +]
    • Changedset_maintenance_offset7 fields changed
      • addedOutput schema / properties / entry / properties / details / additionalProperties
        Added value: +true
      • addedOutput schema / properties / entry / properties / details / properties
        Added value: +{
        +  "from": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "components_spec": {
        +        "type": "string"
        +      },
        +      "nominal_step": {
        +        "type": "integer"
        +      },
        +      "setting_scheme": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "setting_scheme",
        +      "nominal_step",
        +      "components_spec"
        +    ],
        +    "type": "object"
        +  },
        +  "to": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "components_spec": {
        +        "type": "null"
        +      },
        +      "nominal_step": {
        +        "type": "number"
        +      },
        +      "setting_scheme": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "setting_scheme",
        +      "nominal_step",
        +      "components_spec"
        +    ],
        +    "type": "object"
        +  }
        +}
      • addedOutput schema / properties / entry / properties / details / required
        Added value: +[]
      • changedOutput schema / properties / entry / properties / details / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "object"
        +]
      • addedOutput schema / properties / entry / properties / details_known
        Added value: +{
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / entry / properties / evidence / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / entry / required
        Previous value: -[
        -  "id",
        -  "equipment_kind",
        -  "equipment_id",
        -  "task",
        -  "performed_at",
        -  "disturbs_zero",
        -  "note",
        -  "offset_value",
        -  "offset_source",
        -  "offset_at",
        -  "evidence",
        -  "details",
        -  "details_version",
        -  "created_at"
        -]New value: +[
        +  "id",
        +  "equipment_kind",
        +  "equipment_id",
        +  "task",
        +  "performed_at",
        +  "disturbs_zero",
        +  "note",
        +  "offset_value",
        +  "offset_source",
        +  "offset_at",
        +  "evidence",
        +  "details",
        +  "details_version",
        +  "created_at",
        +  "details_known"
        +]
    • Addedupdate_grinder
    • Addedupdate_machine
    • Addedupdate_scale
    • Addedupdate_water
  12. 14 tool updates
    • Changedcompute_age3 fields changed
      • addedInput schema / properties / bag_id
        Added value: +{
        +  "description": "A bag of that coffee (from list_beans' bags[]); null = its current bag",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "bean_id",
        -  "roast_date",
        -  "age_days",
        -  "days_off_roast",
        -  "days_since_opened",
        -  "frozen_days_excluded",
        -  "effective_age_days",
        -  "rest_window",
        -  "verdict",
        -  "grams_remaining",
        -  "warnings"
        -]New value: +[
        +  "bean_id",
        +  "bag_id",
        +  "roast_date",
        +  "age_days",
        +  "days_off_roast",
        +  "days_since_opened",
        +  "frozen_days_excluded",
        +  "effective_age_days",
        +  "rest_window",
        +  "verdict",
        +  "grams_remaining",
        +  "warnings"
        +]
    • Changeddiagnose_preview2 fields changed
      • addedOutput schema / properties / bean_context / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / bean_context / required
        Previous value: -[
        -  "bean_id",
        -  "bean_name",
        -  "roast_level",
        -  "effective_age_days",
        -  "dial_category"
        -]New value: +[
        +  "bean_id",
        +  "bag_id",
        +  "bean_name",
        +  "roast_level",
        +  "effective_age_days",
        +  "dial_category"
        +]
    • Changeddiagnose_shot2 fields changed
      • addedOutput schema / properties / bean_context / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / bean_context / required
        Previous value: -[
        -  "bean_id",
        -  "bean_name",
        -  "roast_level",
        -  "effective_age_days",
        -  "dial_category"
        -]New value: +[
        +  "bean_id",
        +  "bag_id",
        +  "bean_name",
        +  "roast_level",
        +  "effective_age_days",
        +  "dial_category"
        +]
    • Changedget_dial_state10 fields changed
      • addedOutput schema / properties / active_coffee / properties / acidity
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / active_coffee / properties / arabica_pct
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / active_coffee / properties / bag
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "age_verdict": {
        +      "type": "string"
        +    },
        +    "archived": {
        +      "type": "integer"
        +    },
        +    "bag_notes": {
        +      "type": "string"
        +    },
        +    "bag_weight_g": {
        +      "type": "null"
        +    },
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "best_by_date": {
        +      "type": "null"
        +    },
        +    "buy_date": {
        +      "type": "null"
        +    },
        +    "cost": {
        +      "type": "null"
        +    },
        +    "currency": {
        +      "type": "null"
        +    },
        +    "days_off_roast": {
        +      "type": "integer"
        +    },
        +    "days_since_opened": {
        +      "type": "null"
        +    },
        +    "ean": {
        +      "type": "null"
        +    },
        +    "effective_age_days": {
        +      "type": "integer"
        +    },
        +    "finished": {
        +      "type": "integer"
        +    },
        +    "frozen_at": {
        +      "type": "null"
        +    },
        +    "frozen_days_excluded": {
        +      "type": "integer"
        +    },
        +    "frozen_note": {
        +      "type": "null"
        +    },
        +    "frozen_storage": {
        +      "type": "null"
        +    },
        +    "grams_remaining": {
        +      "type": "null"
        +    },
        +    "id": {
        +      "type": "integer"
        +    },
        +    "opened_date": {
        +      "type": "null"
        +    },
        +    "qr_code": {
        +      "type": "null"
        +    },
        +    "roast_date": {
        +      "type": "string"
        +    },
        +    "state": {
        +      "type": "string"
        +    },
        +    "storage": {
        +      "type": "null"
        +    },
        +    "unfrozen_at": {
        +      "type": "null"
        +    },
        +    "weight_adjustment_g": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "bean_id",
        +    "roast_date",
        +    "buy_date",
        +    "opened_date",
        +    "best_by_date",
        +    "bag_weight_g",
        +    "weight_adjustment_g",
        +    "cost",
        +    "currency",
        +    "state",
        +    "finished",
        +    "frozen_at",
        +    "unfrozen_at",
        +    "frozen_storage",
        +    "frozen_note",
        +    "storage",
        +    "ean",
        +    "qr_code",
        +    "bag_notes",
        +    "archived",
        +    "days_off_roast",
        +    "days_since_opened",
        +    "frozen_days_excluded",
        +    "effective_age_days",
        +    "age_verdict",
        +    "grams_remaining"
        +  ],
        +  "type": "object"
        +}
      • addedOutput schema / properties / active_coffee / properties / body
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / active_coffee / properties / current_bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • addedOutput schema / properties / active_coffee / properties / notes
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / active_coffee / properties / sweetness
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / active_coffee / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "roaster",
        -  "origin",
        -  "variety",
        -  "roast_date",
        -  "roast_level",
        -  "process_type",
        -  "dial_category",
        -  "bag_notes",
        -  "rest_window_days_min",
        -  "rest_window_days_max",
        -  "state",
        -  "opened_date",
        -  "storage",
        -  "is_active",
        -  "weight_adjustment_g",
        -  "buy_date",
        -  "bag_weight_g",
        -  "cost",
        -  "currency",
        -  "decaffeinated",
        -  "bean_mix",
        -  "roast_range",
        -  "roast_custom",
        -  "roasting_type",
        -  "aromatics",
        -  "cupping_points",
        -  "rating",
        -  "favourite",
        -  "url",
        -  "ean",
        -  "qr_code",
        -  "co2e_kg",
        -  "attachments",
        -  "archived",
        -  "frozen_at",
        -  "unfrozen_at",
        -  "frozen_storage",
        -  "frozen_note",
        -  "best_by_date",
        -  "finished",
        -  "age_days",
        -  "days_off_roast",
        -  "days_since_opened",
        -  "frozen_days_excluded",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining"
        -]New value: +[
        +  "id",
        +  "name",
        +  "roaster",
        +  "origin",
        +  "variety",
        +  "roast_date",
        +  "roast_level",
        +  "process_type",
        +  "dial_category",
        +  "bag_notes",
        +  "rest_window_days_min",
        +  "rest_window_days_max",
        +  "state",
        +  "opened_date",
        +  "storage",
        +  "is_active",
        +  "weight_adjustment_g",
        +  "buy_date",
        +  "bag_weight_g",
        +  "cost",
        +  "currency",
        +  "decaffeinated",
        +  "bean_mix",
        +  "roast_range",
        +  "roast_custom",
        +  "roasting_type",
        +  "aromatics",
        +  "cupping_points",
        +  "rating",
        +  "favourite",
        +  "url",
        +  "ean",
        +  "qr_code",
        +  "co2e_kg",
        +  "attachments",
        +  "archived",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "best_by_date",
        +  "finished",
        +  "notes",
        +  "arabica_pct",
        +  "body",
        +  "acidity",
        +  "sweetness",
        +  "age_days",
        +  "days_off_roast",
        +  "days_since_opened",
        +  "frozen_days_excluded",
        +  "effective_age_days",
        +  "age_verdict",
        +  "grams_remaining",
        +  "current_bag_id",
        +  "bag"
        +]
      • addedOutput schema / properties / recent_shots / items / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / recent_shots / items / required
        Previous value: -[
        -  "id",
        -  "pulled_at",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "grind_label",
        -  "grind_components",
        -  "grind_position_value",
        -  "dose_g",
        -  "yield_g",
        -  "time_s",
        -  "flow_rate_gps",
        -  "finish_action",
        -  "drink_intent",
        -  "flavor_notes",
        -  "sensory_tags",
        -  "observations",
        -  "temp_taste",
        -  "rating",
        -  "verdict",
        -  "data_confidence",
        -  "temperature_c",
        -  "bloom_time_s",
        -  "first_drip_time_s",
        -  "beverage_g",
        -  "tds",
        -  "ey_pct",
        -  "water_id",
        -  "vessel_name",
        -  "vessel_weight_g",
        -  "favourite",
        -  "best_brew",
        -  "tasted",
        -  "taste_pending"
        -]New value: +[
        +  "id",
        +  "pulled_at",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "grind_label",
        +  "grind_components",
        +  "grind_position_value",
        +  "dose_g",
        +  "yield_g",
        +  "time_s",
        +  "flow_rate_gps",
        +  "finish_action",
        +  "drink_intent",
        +  "flavor_notes",
        +  "sensory_tags",
        +  "observations",
        +  "temp_taste",
        +  "rating",
        +  "verdict",
        +  "data_confidence",
        +  "temperature_c",
        +  "bloom_time_s",
        +  "first_drip_time_s",
        +  "beverage_g",
        +  "tds",
        +  "ey_pct",
        +  "water_id",
        +  "vessel_name",
        +  "vessel_weight_g",
        +  "favourite",
        +  "best_brew",
        +  "tasted",
        +  "bag_id",
        +  "taste_pending"
        +]
    • Changedlist_beans25 fields changed
      • addedOutput schema / properties / beans / items / properties / acidity
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / arabica_pct
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / aromatics
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / bags
        Added value: +{
        +  "items": {
        +    "additionalProperties": true,
        +    "properties": {
        +      "age_verdict": {
        +        "type": "string"
        +      },
        +      "archived": {
        +        "type": "integer"
        +      },
        +      "bag_notes": {
        +        "type": [
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "bag_weight_g": {
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      },
        +      "bean_id": {
        +        "type": "integer"
        +      },
        +      "best_by_date": {
        +        "type": "null"
        +      },
        +      "buy_date": {
        +        "type": "null"
        +      },
        +      "cost": {
        +        "type": [
        +          "null",
        +          "integer"
        +        ]
        +      },
        +      "currency": {
        +        "type": [
        +          "null",
        +          "string"
        +        ]
        +      },
        +      "days_off_roast": {
        +        "type": "integer"
        +      },
        +      "days_since_opened": {
        +        "type": "null"
        +      },
        +      "ean": {
        +        "type": "null"
        +      },
        +      "effective_age_days": {
        +        "type": "integer"
        +      },
        +      "finished": {
        +        "type": "integer"
        +      },
        +      "frozen_at": {
        +        "type": "null"
        +      },
        +      "frozen_days_excluded": {
        +        "type": "integer"
        +      },
        +      "frozen_note": {
        +        "type": "null"
        +      },
        +      "frozen_storage": {
        +        "type": "null"
        +      },
        +      "grams_remaining": {
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      },
        +      "id": {
        +        "type": "integer"
        +      },
        +      "opened_date": {
        +        "type": "null"
        +      },
        +      "qr_code": {
        +        "type": "null"
        +      },
        +      "roast_date": {
        +        "type": "string"
        +      },
        +      "shots_remaining": {
        +        "type": "integer"
        +      },
        +      "shots_remaining_basis": {
        +        "type": "string"
        +      },
        +      "shots_remaining_dose_g": {
        +        "type": [
        +          "integer",
        +          "number"
        +        ]
        +      },
        +      "shots_remaining_sample": {
        +        "type": [
        +          "integer",
        +          "null"
        +        ]
        +      },
        +      "state": {
        +        "type": "string"
        +      },
        +      "storage": {
        +        "type": "null"
        +      },
        +      "unfrozen_at": {
        +        "type": "null"
        +      },
        +      "weight_adjustment_g": {
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "id",
        +      "bean_id",
        +      "roast_date",
        +      "buy_date",
        +      "opened_date",
        +      "best_by_date",
        +      "bag_weight_g",
        +      "weight_adjustment_g",
        +      "cost",
        +      "currency",
        +      "state",
        +      "finished",
        +      "frozen_at",
        +      "unfrozen_at",
        +      "frozen_storage",
        +      "frozen_note",
        +      "storage",
        +      "ean",
        +      "qr_code",
        +      "bag_notes",
        +      "archived",
        +      "effective_age_days",
        +      "age_verdict",
        +      "grams_remaining",
        +      "days_since_opened",
        +      "days_off_roast",
        +      "frozen_days_excluded"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / beans / items / properties / best_by_date
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / body
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / buy_date
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / cupping_points
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / current_bag_id
        Added value: +{
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
      • addedOutput schema / properties / beans / items / properties / ean
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / frozen_at
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / frozen_note
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / frozen_storage
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / notes
        Added value: +{
        +  "type": [
        +    "null",
        +    "string"
        +  ]
        +}
      • addedOutput schema / properties / beans / items / properties / origin
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / origins / items / properties / elevation_max_m
        Added value: +{
        +  "type": "null"
        +}
      • changedOutput schema / properties / beans / items / properties / origins / items / required
        Previous value: -[
        -  "country",
        -  "region",
        -  "farm",
        -  "farmer",
        -  "variety",
        -  "processing",
        -  "elevation_m",
        -  "harvest",
        -  "percentage"
        -]New value: +[
        +  "country",
        +  "region",
        +  "farm",
        +  "farmer",
        +  "variety",
        +  "processing",
        +  "elevation_m",
        +  "elevation_max_m",
        +  "harvest",
        +  "percentage"
        +]
      • addedOutput schema / properties / beans / items / properties / qr_code
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / roasting_type
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / sweetness
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / unfrozen_at
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / url
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / variety
        Added value: +{
        +  "type": "null"
        +}
      • addedOutput schema / properties / beans / items / properties / weight_adjustment_g
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / beans / items / required
        Previous value: -[
        -  "id",
        -  "name",
        -  "roaster",
        -  "roast_date",
        -  "roast_level",
        -  "state",
        -  "opened_date",
        -  "archived",
        -  "finished",
        -  "favourite",
        -  "decaffeinated",
        -  "bag_weight_g",
        -  "process_type",
        -  "dial_category",
        -  "rest_window_days_min",
        -  "rest_window_days_max",
        -  "storage",
        -  "bag_notes",
        -  "cost",
        -  "currency",
        -  "rating",
        -  "effective_age_days",
        -  "age_verdict",
        -  "grams_remaining",
        -  "days_since_opened",
        -  "days_off_roast",
        -  "frozen_days_excluded",
        -  "origins"
        -]New value: +[
        +  "id",
        +  "name",
        +  "roaster",
        +  "roast_level",
        +  "archived",
        +  "favourite",
        +  "decaffeinated",
        +  "process_type",
        +  "dial_category",
        +  "rest_window_days_min",
        +  "rest_window_days_max",
        +  "rating",
        +  "aromatics",
        +  "cupping_points",
        +  "roasting_type",
        +  "variety",
        +  "origin",
        +  "url",
        +  "notes",
        +  "arabica_pct",
        +  "body",
        +  "acidity",
        +  "sweetness",
        +  "roast_date",
        +  "buy_date",
        +  "opened_date",
        +  "best_by_date",
        +  "bag_weight_g",
        +  "weight_adjustment_g",
        +  "cost",
        +  "currency",
        +  "state",
        +  "finished",
        +  "frozen_at",
        +  "unfrozen_at",
        +  "frozen_storage",
        +  "frozen_note",
        +  "storage",
        +  "ean",
        +  "qr_code",
        +  "bag_notes",
        +  "effective_age_days",
        +  "age_verdict",
        +  "grams_remaining",
        +  "days_since_opened",
        +  "days_off_roast",
        +  "frozen_days_excluded",
        +  "current_bag_id",
        +  "bags",
        +  "origins"
        +]
    • Changedlist_recipes2 fields changed
      • addedOutput schema / properties / recipes / items / properties / roaster_name
        Added value: +{
        +  "type": "string"
        +}
      • changedOutput schema / properties / recipes / items / required
        Previous value: -[
        -  "id",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "status",
        -  "grind_label",
        -  "grind_position_value",
        -  "dose_g",
        -  "target_yield_g",
        -  "target_time_s",
        -  "finish_action",
        -  "drink_intent",
        -  "profile",
        -  "locked_at",
        -  "bean_name",
        -  "grinder_name",
        -  "machine_name",
        -  "grind_reading"
        -]New value: +[
        +  "id",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "status",
        +  "grind_label",
        +  "grind_position_value",
        +  "dose_g",
        +  "target_yield_g",
        +  "target_time_s",
        +  "finish_action",
        +  "drink_intent",
        +  "profile",
        +  "locked_at",
        +  "bean_name",
        +  "roaster_name",
        +  "grinder_name",
        +  "machine_name",
        +  "grind_reading"
        +]
    • Changedlist_shots3 fields changed
      • addedInput schema / properties / filters / properties / bag_id
        Added value: +{
        +  "description": "Filter by bag number (one purchase of a coffee)",
        +  "type": "integer"
        +}
      • addedOutput schema / properties / shots / items / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / shots / items / required
        Previous value: -[
        -  "id",
        -  "pulled_at",
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "program_id",
        -  "grind_label",
        -  "grind_components",
        -  "grind_position_value",
        -  "dose_g",
        -  "yield_g",
        -  "time_s",
        -  "flow_rate_gps",
        -  "finish_action",
        -  "drink_intent",
        -  "flavor_notes",
        -  "sensory_tags",
        -  "observations",
        -  "temp_taste",
        -  "rating",
        -  "verdict",
        -  "data_confidence",
        -  "temperature_c",
        -  "bloom_time_s",
        -  "first_drip_time_s",
        -  "beverage_g",
        -  "tds",
        -  "ey_pct",
        -  "water_id",
        -  "vessel_name",
        -  "vessel_weight_g",
        -  "favourite",
        -  "best_brew",
        -  "tasted",
        -  "bean_name",
        -  "taste_pending"
        -]New value: +[
        +  "id",
        +  "pulled_at",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "grind_label",
        +  "grind_components",
        +  "grind_position_value",
        +  "dose_g",
        +  "yield_g",
        +  "time_s",
        +  "flow_rate_gps",
        +  "finish_action",
        +  "drink_intent",
        +  "flavor_notes",
        +  "sensory_tags",
        +  "observations",
        +  "temp_taste",
        +  "rating",
        +  "verdict",
        +  "data_confidence",
        +  "temperature_c",
        +  "bloom_time_s",
        +  "first_drip_time_s",
        +  "beverage_g",
        +  "tds",
        +  "ey_pct",
        +  "water_id",
        +  "vessel_name",
        +  "vessel_weight_g",
        +  "favourite",
        +  "best_brew",
        +  "tasted",
        +  "bag_id",
        +  "bean_name",
        +  "taste_pending"
        +]
    • Changedlog_shot1 field changed
      • addedInput schema / properties / bag_id
        Added value: +{
        +  "description": "Which bag of the coffee the shot came from; defaults to its current (open) bag. Bag numbers are on list_beans' bags[]",
        +  "type": [
        +    "integer",
        +    "null"
        +  ]
        +}
    • Addedmerge_beans
    • Addedregister_bag
    • Changedregister_coffee8 fields changed
      • addedInput schema / properties / acidity
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • addedInput schema / properties / arabica_pct
        Added value: +{
        +  "description": "Percent arabica, 100 for a pure arabica; the rest robusta",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • addedInput schema / properties / body
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • addedInput schema / properties / notes
        Added value: +{
        +  "description": "The coffee's own description — the roaster's tasting notes, the dialing approach that worked. Per bag notes go in bag_notes.",
        +  "maxLength": 4096,
        +  "type": "string"
        +}
      • addedInput schema / properties / origins / items / properties / elevation_max_m
        Added value: +{
        +  "description": "Upper end of an elevation range, e.g. 1980 for \"1800–1980 m\"; elevation_m is the lower end",
        +  "type": "number"
        +}
      • addedInput schema / properties / sweetness
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • addedOutput schema / properties / bag_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "status",
        -  "bean_id",
        -  "message"
        -]New value: +[
        +  "status",
        +  "bean_id",
        +  "bag_id",
        +  "message"
        +]
    • Addedupdate_bag
    • Changedupdate_bean7 fields changed
      • addedInput schema / properties / fields / properties / acidity
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • addedInput schema / properties / fields / properties / arabica_pct
        Added value: +{
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • addedInput schema / properties / fields / properties / body
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • addedInput schema / properties / fields / properties / notes
        Added value: +{
        +  "description": "The coffee's own description — roaster tasting notes, the dialing approach. Replaces the stored value; the optional reason is appended instead.",
        +  "maxLength": 4096,
        +  "type": "string"
        +}
      • addedInput schema / properties / fields / properties / origins / items / properties / elevation_max_m
        Added value: +{
        +  "description": "Upper end of an elevation range; elevation_m is the lower end",
        +  "type": "number"
        +}
      • addedInput schema / properties / fields / properties / sweetness
        Added value: +{
        +  "description": "Free text; roasters usually say low, medium or high",
        +  "type": "string"
        +}
      • changedInput schema / properties / reason / description
        Previous value: -"Optional free-text note, appended to the bag notes as an ordinary line. Omit it and nothing is written."New value: +"Optional free-text note, appended as an ordinary line to the coffee's notes — or to the current bag's bag_notes when the call changed only bag fields. Omit it and nothing is written."
    • Changedupdate_shot2 fields changed
      • addedInput schema / properties / fields / properties / bag_id
        Added value: +{
        +  "description": "Re-file the shot onto this bag; must be a bag of the shot's bean",
        +  "type": "integer"
        +}
      • changedInput schema / properties / fields / properties / bean_id / description
        Previous value: -"Re-file the shot onto this bean"New value: +"Re-file the shot onto this bean (its current bag, unless bag_id is also given)"
  13. 1 tool update
    • Changedset_active4 fields changed
      • changedInput schema / properties / bean_id / description
        Previous value: -"null = leave unchanged"New value: +"This account's own bean number, as returned by list_beans. Never guess one; null = leave unchanged"
      • changedInput schema / properties / grinder_id / description
        Previous value: -"null = leave unchanged"New value: +"This account's own grinder number, as returned by list_grinders. Never guess one; null = leave unchanged"
      • changedInput schema / properties / machine_id / description
        Previous value: -"null = leave unchanged"New value: +"This account's own machine number, as returned by list_machines. Never guess one; null = leave unchanged"
      • changedInput schema / properties / program_id / description
        Previous value: -"null = leave unchanged"New value: +"A program number from the active machine's list. null = leave unchanged"
  14. 9 tool updates
    • Changedget_dial_state6 fields changed
      • addedOutput schema / properties / locked_recipe / additionalProperties
        Added value: +true
      • addedOutput schema / properties / locked_recipe / properties
        Added value: +{
        +  "bean_id": {
        +    "type": "integer"
        +  },
        +  "dose_g": {
        +    "type": "integer"
        +  },
        +  "drink_intent": {
        +    "type": "string"
        +  },
        +  "finish_action": {
        +    "type": "string"
        +  },
        +  "grind_label": {
        +    "type": "string"
        +  },
        +  "grind_position_value": {
        +    "type": "integer"
        +  },
        +  "grind_reading": {
        +    "type": "null"
        +  },
        +  "grinder_id": {
        +    "type": "integer"
        +  },
        +  "id": {
        +    "type": "integer"
        +  },
        +  "kb_version": {
        +    "type": "string"
        +  },
        +  "locked_at": {
        +    "type": "string"
        +  },
        +  "machine_id": {
        +    "type": "integer"
        +  },
        +  "profile": {
        +    "type": "string"
        +  },
        +  "program_id": {
        +    "type": "null"
        +  },
        +  "status": {
        +    "type": "string"
        +  },
        +  "target_time_s": {
        +    "type": "integer"
        +  },
        +  "target_yield_g": {
        +    "type": "integer"
        +  }
        +}
      • addedOutput schema / properties / locked_recipe / required
        Added value: +[
        +  "id",
        +  "bean_id",
        +  "grinder_id",
        +  "machine_id",
        +  "program_id",
        +  "status",
        +  "grind_label",
        +  "grind_position_value",
        +  "dose_g",
        +  "target_yield_g",
        +  "target_time_s",
        +  "finish_action",
        +  "drink_intent",
        +  "profile",
        +  "kb_version",
        +  "locked_at",
        +  "grind_reading"
        +]
      • changedOutput schema / properties / locked_recipe / type
        Previous value: -"null"New value: +"object"
      • addedOutput schema / properties / recent_recommendations / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "based_on_shot": {
        +      "type": "null"
        +    },
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "cited_rules": {
        +      "type": "string"
        +    },
        +    "confidence": {
        +      "type": "string"
        +    },
        +    "created_at": {
        +      "type": "string"
        +    },
        +    "direction": {
        +      "type": "string"
        +    },
        +    "grinder_id": {
        +      "type": "integer"
        +    },
        +    "kb_version": {
        +      "type": "string"
        +    },
        +    "lever": {
        +      "type": "string"
        +    },
        +    "machine_id": {
        +      "type": "integer"
        +    },
        +    "rationale": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "created_at",
        +    "bean_id",
        +    "grinder_id",
        +    "machine_id",
        +    "based_on_shot",
        +    "lever",
        +    "direction",
        +    "rationale",
        +    "confidence",
        +    "cited_rules",
        +    "kb_version"
        +  ],
        +  "type": "object"
        +}
      • addedOutput schema / properties / recent_shots / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "best_brew": {
        +      "type": "integer"
        +    },
        +    "beverage_g": {
        +      "type": "null"
        +    },
        +    "bloom_time_s": {
        +      "type": "null"
        +    },
        +    "data_confidence": {
        +      "type": "string"
        +    },
        +    "dose_g": {
        +      "type": "integer"
        +    },
        +    "drink_intent": {
        +      "type": "string"
        +    },
        +    "ey_pct": {
        +      "type": "null"
        +    },
        +    "favourite": {
        +      "type": "integer"
        +    },
        +    "finish_action": {
        +      "type": "string"
        +    },
        +    "first_drip_time_s": {
        +      "type": "null"
        +    },
        +    "flavor_notes": {
        +      "type": "string"
        +    },
        +    "flow_rate_gps": {
        +      "type": "number"
        +    },
        +    "grind_components": {
        +      "type": "string"
        +    },
        +    "grind_label": {
        +      "type": "string"
        +    },
        +    "grind_position_value": {
        +      "type": "integer"
        +    },
        +    "grinder_id": {
        +      "type": "integer"
        +    },
        +    "id": {
        +      "type": "integer"
        +    },
        +    "machine_id": {
        +      "type": "integer"
        +    },
        +    "observations": {
        +      "type": "string"
        +    },
        +    "program_id": {
        +      "type": "null"
        +    },
        +    "pulled_at": {
        +      "type": "string"
        +    },
        +    "rating": {
        +      "type": "null"
        +    },
        +    "sensory_tags": {
        +      "type": "string"
        +    },
        +    "taste_pending": {
        +      "type": "integer"
        +    },
        +    "tasted": {
        +      "type": "integer"
        +    },
        +    "tds": {
        +      "type": "null"
        +    },
        +    "temp_taste": {
        +      "type": "string"
        +    },
        +    "temperature_c": {
        +      "type": "null"
        +    },
        +    "time_s": {
        +      "type": "integer"
        +    },
        +    "verdict": {
        +      "type": "null"
        +    },
        +    "vessel_name": {
        +      "type": "null"
        +    },
        +    "vessel_weight_g": {
        +      "type": "null"
        +    },
        +    "water_id": {
        +      "type": "null"
        +    },
        +    "yield_g": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "pulled_at",
        +    "bean_id",
        +    "grinder_id",
        +    "machine_id",
        +    "program_id",
        +    "grind_label",
        +    "grind_components",
        +    "grind_position_value",
        +    "dose_g",
        +    "yield_g",
        +    "time_s",
        +    "flow_rate_gps",
        +    "finish_action",
        +    "drink_intent",
        +    "flavor_notes",
        +    "sensory_tags",
        +    "observations",
        +    "temp_taste",
        +    "rating",
        +    "verdict",
        +    "data_confidence",
        +    "temperature_c",
        +    "bloom_time_s",
        +    "first_drip_time_s",
        +    "beverage_g",
        +    "tds",
        +    "ey_pct",
        +    "water_id",
        +    "vessel_name",
        +    "vessel_weight_g",
        +    "favourite",
        +    "best_brew",
        +    "tasted",
        +    "taste_pending"
        +  ],
        +  "type": "object"
        +}
    • Changedget_stats3 fields changed
      • changedOutput schema / properties / last_bean / type
        Previous value: -"null"New value: +"string"
      • changedOutput schema / properties / last_grind_setting / type
        Previous value: -"null"New value: +"string"
      • changedOutput schema / properties / last_used / type
        Previous value: -"null"New value: +"string"
    • Changedlist_beans10 fields changed
      • changedOutput schema / properties / beans / items / properties / bag_notes / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / beans / items / properties / bag_weight_g / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / properties / decaffeinated / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / properties / favourite / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / properties / grams_remaining / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • addedOutput schema / properties / beans / items / properties / origins / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "country": {
        +      "type": "string"
        +    },
        +    "elevation_m": {
        +      "type": "null"
        +    },
        +    "farm": {
        +      "type": "null"
        +    },
        +    "farmer": {
        +      "type": "null"
        +    },
        +    "harvest": {
        +      "type": "null"
        +    },
        +    "percentage": {
        +      "type": "integer"
        +    },
        +    "processing": {
        +      "type": "null"
        +    },
        +    "region": {
        +      "type": "string"
        +    },
        +    "variety": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "country",
        +    "region",
        +    "farm",
        +    "farmer",
        +    "variety",
        +    "processing",
        +    "elevation_m",
        +    "harvest",
        +    "percentage"
        +  ],
        +  "type": "object"
        +}
      • changedOutput schema / properties / beans / items / properties / rest_window_days_max / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / properties / rest_window_days_min / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / beans / items / properties / shots_remaining_dose_g / type
        Previous value: -"number"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / beans / items / properties / shots_remaining_sample / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
    • Changedlist_grinders27 fields changed
      • changedOutput schema / properties / grinders / items / properties / components_spec / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_at / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / epoch_id / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / evidence / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_at / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_source / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / offset_value / type
        Previous value: -"null"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / reference_recipe_id / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / basis / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / done / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / every / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / last_at / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / ratio / type
        Previous value: -"number"New value: +"integer"
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / due / properties / burr_clean / properties / remaining / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / evidence / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / note / type
        Previous value: -[
        -  "string",
        -  "null"
        -]New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_at / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_source / type
        Previous value: -[
        -  "null",
        -  "string"
        -]New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / entries / items / properties / offset_value / type
        Previous value: -[
        -  "null",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / max_value / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / microns_per_step / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / min_value / type
        Previous value: -"null"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedOutput schema / properties / grinders / items / properties / position_value / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / grinders / items / properties / setting_components / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / setting_label / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / source / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / grinders / items / properties / verified_at / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
    • Changedlist_machines5 fields changed
      • changedOutput schema / properties / machines / items / properties / basket_size_grams / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / machines / items / properties / boiler_type / type
        Previous value: -"string"New value: +[
        +  "null",
        +  "string"
        +]
      • changedOutput schema / properties / machines / items / properties / prep_style / type
        Previous value: -"null"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / prep_type / type
        Previous value: -"null"New value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / machines / items / properties / programs / items / properties / description / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedlist_recipes5 fields changed
      • changedOutput schema / properties / recipes / items / properties / dose_g / type
        Previous value: -"number"New value: +[
        +  "integer",
        +  "number"
        +]
      • changedOutput schema / properties / recipes / items / properties / grind_reading / required
        Previous value: -[
        -  "label",
        -  "offset_clicks",
        -  "offset_direction",
        -  "epochs_since",
        -  "unmeasured"
        -]New value: +[]
      • changedOutput schema / properties / recipes / items / properties / grind_reading / type
        Previous value: -"object"New value: +[
        +  "null",
        +  "object"
        +]
      • changedOutput schema / properties / recipes / items / properties / program_id / type
        Previous value: -"integer"New value: +[
        +  "null",
        +  "integer"
        +]
      • changedOutput schema / properties / recipes / items / properties / target_yield_g / type
        Previous value: -[
        -  "number",
        -  "integer"
        -]New value: +[
        +  "integer",
        +  "number"
        +]
    • Changedlist_scales1 field changed
      • addedOutput schema / properties / scales / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "archived": {
        +      "type": "integer"
        +    },
        +    "auto_tare": {
        +      "type": "null"
        +    },
        +    "auto_timer": {
        +      "type": "null"
        +    },
        +    "capacity_g": {
        +      "type": "null"
        +    },
        +    "connectivity": {
        +      "type": "null"
        +    },
        +    "depth_mm": {
        +      "type": "null"
        +    },
        +    "flow_rate_display": {
        +      "type": "null"
        +    },
        +    "height_mm": {
        +      "type": "null"
        +    },
        +    "id": {
        +      "type": "integer"
        +    },
        +    "name": {
        +      "type": "string"
        +    },
        +    "notes": {
        +      "type": "null"
        +    },
        +    "power": {
        +      "type": "null"
        +    },
        +    "resolution_banded": {
        +      "type": "integer"
        +    },
        +    "resolution_g": {
        +      "type": "number"
        +    },
        +    "scale_type": {
        +      "type": "string"
        +    },
        +    "water_resistance": {
        +      "type": "null"
        +    },
        +    "width_mm": {
        +      "type": "null"
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "name",
        +    "scale_type",
        +    "resolution_g",
        +    "resolution_banded",
        +    "capacity_g",
        +    "width_mm",
        +    "depth_mm",
        +    "height_mm",
        +    "water_resistance",
        +    "connectivity",
        +    "flow_rate_display",
        +    "auto_tare",
        +    "auto_timer",
        +    "power",
        +    "notes",
        +    "archived"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_shots1 field changed
      • addedOutput schema / properties / shots / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "bean_name": {
        +      "type": "string"
        +    },
        +    "best_brew": {
        +      "type": "integer"
        +    },
        +    "beverage_g": {
        +      "type": "null"
        +    },
        +    "bloom_time_s": {
        +      "type": "null"
        +    },
        +    "data_confidence": {
        +      "type": "string"
        +    },
        +    "dose_g": {
        +      "type": "integer"
        +    },
        +    "drink_intent": {
        +      "type": "string"
        +    },
        +    "ey_pct": {
        +      "type": "null"
        +    },
        +    "favourite": {
        +      "type": "integer"
        +    },
        +    "finish_action": {
        +      "type": "string"
        +    },
        +    "first_drip_time_s": {
        +      "type": "null"
        +    },
        +    "flavor_notes": {
        +      "type": "string"
        +    },
        +    "flow_rate_gps": {
        +      "type": "number"
        +    },
        +    "grind_components": {
        +      "type": "string"
        +    },
        +    "grind_label": {
        +      "type": "string"
        +    },
        +    "grind_position_value": {
        +      "type": "integer"
        +    },
        +    "grinder_id": {
        +      "type": "integer"
        +    },
        +    "id": {
        +      "type": "integer"
        +    },
        +    "machine_id": {
        +      "type": "integer"
        +    },
        +    "observations": {
        +      "type": "string"
        +    },
        +    "program_id": {
        +      "type": "null"
        +    },
        +    "pulled_at": {
        +      "type": "string"
        +    },
        +    "rating": {
        +      "type": "null"
        +    },
        +    "sensory_tags": {
        +      "type": "string"
        +    },
        +    "taste_pending": {
        +      "type": "integer"
        +    },
        +    "tasted": {
        +      "type": "integer"
        +    },
        +    "tds": {
        +      "type": "null"
        +    },
        +    "temp_taste": {
        +      "type": "string"
        +    },
        +    "temperature_c": {
        +      "type": "null"
        +    },
        +    "time_s": {
        +      "type": "integer"
        +    },
        +    "verdict": {
        +      "type": "null"
        +    },
        +    "vessel_name": {
        +      "type": "null"
        +    },
        +    "vessel_weight_g": {
        +      "type": "null"
        +    },
        +    "water_id": {
        +      "type": "null"
        +    },
        +    "yield_g": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "pulled_at",
        +    "bean_id",
        +    "grinder_id",
        +    "machine_id",
        +    "program_id",
        +    "grind_label",
        +    "grind_components",
        +    "grind_position_value",
        +    "dose_g",
        +    "yield_g",
        +    "time_s",
        +    "flow_rate_gps",
        +    "finish_action",
        +    "drink_intent",
        +    "flavor_notes",
        +    "sensory_tags",
        +    "observations",
        +    "temp_taste",
        +    "rating",
        +    "verdict",
        +    "data_confidence",
        +    "temperature_c",
        +    "bloom_time_s",
        +    "first_drip_time_s",
        +    "beverage_g",
        +    "tds",
        +    "ey_pct",
        +    "water_id",
        +    "vessel_name",
        +    "vessel_weight_g",
        +    "favourite",
        +    "best_brew",
        +    "tasted",
        +    "bean_name",
        +    "taste_pending"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_waters1 field changed
      • addedOutput schema / properties / waters / items
        Added value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "archived": {
        +      "type": "integer"
        +    },
        +    "calcium": {
        +      "type": "null"
        +    },
        +    "gh": {
        +      "type": "integer"
        +    },
        +    "id": {
        +      "type": "integer"
        +    },
        +    "kh": {
        +      "type": "integer"
        +    },
        +    "magnesium": {
        +      "type": "null"
        +    },
        +    "name": {
        +      "type": "string"
        +    },
        +    "notes": {
        +      "type": "null"
        +    },
        +    "sodium": {
        +      "type": "null"
        +    },
        +    "tds": {
        +      "type": "null"
        +    },
        +    "type": {
        +      "type": "null"
        +    }
        +  },
        +  "required": [
        +    "id",
        +    "name",
        +    "type",
        +    "tds",
        +    "gh",
        +    "kh",
        +    "sodium",
        +    "calcium",
        +    "magnesium",
        +    "notes",
        +    "archived"
        +  ],
        +  "type": "object"
        +}
  15. 1 tool update
    • Changedlist_grinders2 fields changed
      • addedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / properties / reference_recipe_id
        Added value: +{
        +  "type": "integer"
        +}
      • changedOutput schema / properties / grinders / items / properties / maintenance / properties / calibration / required
        Previous value: -[
        -  "stale_recipe_count",
        -  "state",
        -  "epoch_id",
        -  "epoch_at",
        -  "offset_value",
        -  "offset_unit",
        -  "offset_source",
        -  "offset_at",
        -  "evidence"
        -]New value: +[
        +  "stale_recipe_count",
        +  "reference_recipe_id",
        +  "state",
        +  "epoch_id",
        +  "epoch_at",
        +  "offset_value",
        +  "offset_unit",
        +  "offset_source",
        +  "offset_at",
        +  "evidence"
        +]

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Remote MCP server that lets AI assistants read tracked competitive intelligence — competitor news, reviews, website changes, social posts and ads — and act on it by managing projects, competitors, battle cards, reports and canvases. Every answer carries a source and a date, and write actions only publish when explicitly shared.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables brand visibility monitoring across major AI platforms like ChatGPT, Claude, Gemini, and Perplexity. It allows users to track visibility scores, analyze competitor data, and receive actionable insights to improve AI-generated brand recommendations.
    16
    23 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Browse IndustryLens's published competitive-intelligence reports and head-to-head competitor comparisons from any AI agent — real, source-backed data.
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources