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 40 days
Last Tested
Transport
Streamable HTTP · MCP 2025-06-18
URL

TDQS

A3.6/5.0

Scored across 41 tools

Disambiguation4/5

Most tools are clearly separated by resource and action, and descriptions cross-reference related tools (register_coffee vs register_bag, update_bean vs update_bag, diagnose_shot vs diagnose_preview). A few pairs could still be confused, especially get_dial_state (which includes recommendations) and suggest_next_step, plus log_maintenance and set_maintenance_offset, but overall boundaries are well drawn.

Naming Consistency4/5

The dominant pattern is lowercase snake_case verb_noun: register_*, update_*, list_*, set_*, log_*. A few names break the verb-first convention (grinder_math, kb_changelog), and the list_* versus get_* split is subtle but consistent enough. The naming is predictable and readable overall.

Tool Count2/5

41 tools is a very large surface for an MCP server, far beyond the typical well-scoped range. While the domain is broad, many tools are near-duplicates in shape (register_grinder/machine/scale/water/program, update_* for nearly every resource) and could likely be consolidated.

Completeness4/5

The shot lifecycle is well covered with log, update, delete, list, and diagnosis, and bean/bag tracking has registration, updates, merging, and state changes. Minor gaps exist—no single-shot get, no direct way to delete a recipe, no delete for beans/equipment beyond archiving/merging—but agents can complete core workflows without dead ends.

Available Tools

41 tools
compute_ageCompute coffee ageA
Read-onlyIdempotent
Inspect

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

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

Output Schema

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

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already establish a safe, read-only, idempotent, closed-world operation, so the bar is low. The description adds real substance: exactly what is computed (days off roast) and the verdict categories (resting/staling), plus the defaulting rule for bag selection. Return shape is covered by the output schema.

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 dense sentence that front-loads the computed output before the defaulting clause. The em-dash construction is slightly awkward but nothing is wasted.

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 annotations carrying the safety profile and an output schema carrying return values, the description only needs to explain inputs and intent. It handles the bag defaulting well but leaves the required bean_id entirely unexplained, a minor but real 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 coverage is 50%: bag_id is already documented in the schema, and the description usefully reinforces the null-means-current-bag default. However, bean_id is required yet described nowhere in either the schema or the description, so the gap is only partially compensated.

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 (compute) and resource (a bag's age in days off roast) plus the derived output (resting/staling verdict). No sibling tool overlaps with this function, so the agent can distinguish it immediately.

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 states the default resolution: 'the coffee's current bag by default, or the bag named.' That tells the agent when the optional bag_id matters. It stops short of naming an alternative tool or exclusions, but no sibling competes for this job.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
shot_idYesID of the shot to delete

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
deleted_shot_idYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already carry destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the bar is lower. The description still adds genuinely new behavioral info: the cascade side effect 'Restores the bean's remaining weight (which is derived from logged doses)' and the irreversibility warning 'Hard delete — there is no undo.' No contradiction with idempotentHint, since re-deleting an already-removed shot would simply no-op or fail.

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, each earning its place: purpose, side effect, and alternative routing. The hard-delete warning is front-loaded before the update_shot guidance, and there is zero fluff, repetition, or restatement of the title.

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 behavioral core is covered: purpose, weight-restoration side effect, irreversibility, and the preferred alternative. The output schema exists so return-value detail is not required. A minor gap is behavior for a non-existent or already-deleted shot_id, but idempotentHint and the output schema mitigate that. For a one-parameter tool, this is complete enough.

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% — the schema itself documents shot_id as 'ID of the shot to delete.' The description adds no parameter-level detail beyond that (no validation rules, no edge-case handling), so the baseline of 3 applies per the coverage rule.

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 ('Delete') with a precise object ('a logged shot from history'), clearly scoping the operation to shot history entries. It distinguishes itself from update_shot by implication, and the title is reinforced rather than merely restated.

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 agents away from destructive misuse: 'To fix a mistake on an otherwise-valid shot, prefer update_shot over delete-and-relog.' This names the exact alternative and the condition that should select it, giving clear when-to-use vs when-not-to-use guidance.

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 — 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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
factsYes
categoryYes
warningsYes
hypothesesYes
kb_versionYes
bean_contextYes
one_variable_onlyYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, and the description adds concrete effect-level context: no verdict and no recommendation-trail entry are written, it is sandbox-only, and with shot_id the metric params are ignored while the shot's own bean and pulled_at age are used. That is meaningful beyond the safety hints, though permissions/rate behavior are unaddressed.

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 key differentiator ('Sandbox-only diagnosis, writes nothing') and keeps to two dense sentences with no filler. It is information-heavy, but every clause carries load; a slight trim of the mode examples would tighten it further.

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 no explanation, and the description still notes the output matches diagnose_shot including bean_context. With optional-everything params (0 required), the two-mode contract is fully covered 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?

Schema coverage is 100%, so the baseline is 3. The description earns above baseline by explaining cross-parameter semantics the schema does not: that shot_id ignores the metric params, that sensory_tags replaces stored tags in preview mode ([] = none), and that bean age at pulled_at is used.

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', dry-run preview) and immediately distinguishes it from the sibling diagnose_shot by noting it writes nothing and shares the same output shape. An agent can separate this from diagnose_shot and log_shot without opening a 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?

Clearly enumerates the two invocation modes and the condition selecting each (pass shot_id for a logged shot vs the full metric set for a hypothetical). It implies diagnose_shot is the non-preview counterpart but never names it as the alternative for a real run, so it stops short of full when/when-not routing.

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".

ParametersJSON Schema
NameRequiredDescriptionDefault
shot_idNoOptional shot ID; defaults to last pulled shot.

Output Schema

ParametersJSON Schema
NameRequiredDescription
factsYes
categoryYes
warningsYes
hypothesesYes
kb_versionYes
bean_contextYes
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 safety (readOnly, idempotent), but the description adds substantial behavioral context: the engine reads metrics and sensory tags rather than free-text notes, bean age is computed at pulled_at and echoed as bean_context, and never uses the active profile. These are non-obvious traits that materially affect correct invocation.

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 purpose and outputs, followed by the bean-resolution guarantee and the critical sensory_tags prerequisite. Dense but each clause carries real information; the final sentence is long yet justified.

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 not be explained, and the description covers the preconditions and the bean-context behavior an agent needs. Nothing required to call this 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?

With a single parameter at 100% schema coverage, the schema already documents that shot_id defaults to the last pulled shot. The description adds no syntax or format detail beyond that, so the baseline of 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 ('Evaluate shot metrics') plus the concrete outputs (ranked hypotheses C6, flip-flop/fatigue warnings). An agent can distinguish this from list_shots or get_stats 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?

Gives an explicit prerequisite chain: taste feedback must be recorded as sensory_tags via log_shot or update_shot before diagnosing, otherwise results are wrong. It also reassures that diagnosing older or differently-filed shots is safe, covering the main decision points.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
recent_shotsYes
active_coffeeYes
locked_recipeYes
active_grinderYes
active_machineYes
active_programYes
recent_recommendationsYes

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, destructiveHint=false and openWorldHint=false, so the safety profile is covered. The description adds that the response bundles live state, active context, recent shots, recommendations and locked recipes, but does not say how 'recent' is bounded or whether the snapshot is cached or live.

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 front-loaded sentence with no filler; the enumeration of returned items is the minimum needed to characterize the payload.

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 zero params, rich annotations, and an output schema covering return values, the description needs only to frame what the tool is for. It does that adequately, though it leaves the semantics of 'current' and 'recent' unspecified.

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 takes zero parameters, so per the rubric the baseline is 4. There is nothing for the description to disambiguate on the input side.

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 ('Retrieve') and resource ('current dialing state') and enumerates the payload components, which distinguishes it from the more statistical get_stats and the prescriptive suggest_next_step. It does not explicitly name siblings, so it falls short of a 5.

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 guidance on when to call this versus get_stats, get_rule, or suggest_next_step, and no stated preconditions. The agent must infer usage purely from the name and the enumerated contents.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON 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_ruleGet a dialing ruleA
Read-onlyIdempotent
Inspect

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

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

Output Schema

ParametersJSON 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesThe entity ID
scopeYesThe stats scope

Output Schema

ParametersJSON 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).

ParametersJSON Schema
NameRequiredDescriptionDefault
to_labelYes
from_labelYes
grinder_idYes

Output Schema

ParametersJSON 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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON 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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
beansYes

TDQS

A3.6/5.0
Behavior4/5

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

Annotations already declare readOnlyHint/idempotent/non-destructive, so safety is covered. The description goes well beyond that, disclosing the row shape (bags[], current_bag_id, top-level fields belonging to the current bag) and the id-numbering semantics — context an agent cannot get from 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?

Front-loaded with the purpose, then dense but useful detail on data shape and id semantics. Every sentence carries information; the density makes it slightly hard to scan but nothing is wasted.

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 needn't be re-explained, and the description still resolves the genuinely ambiguous parts: how bags nest, that top-level freshness fields belong to the current bag, and that the row id differs from bag ids. Nothing an agent needs to call or interpret this tool 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 single enum parameter is fully documented in the schema, so the baseline is 3. The description only restates 'with status filter' and adds no syntax or default detail beyond what the schema already provides.

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 the coffees registered for the account') plus the status-filter scope. The noun 'coffees' naturally separates it from sibling list_* tools, but the description never explicitly names an alternative or contrast, so it stops short of a 5.

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 mentions a status filter but gives no guidance on when to use 'archived' or 'all' versus the default, and never contrasts this with siblings like list_shots or merge_beans. No when-not guidance and no prerequisites are stated.

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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
grindersYes

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already cover read-only, idempotent, non-destructive behavior. The description adds valuable context beyond those annotations by explaining the `id` semantics: account-local numbering starting at 1, safe to display, and expected by other tools. This helps the agent understand how returned values relate to other operations.

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 concise sentences with no filler. The main purpose is front-loaded, and the second sentence provides a key behavioral detail about the returned identifier without over-explaining.

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 one-parameter, read-only list tool with a rich output schema and strong annotations, the description is complete. It clarifies account scoping, explains the id contract, and leaves no essential caller-facing 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?

The input schema provides 100% coverage: the single `status` parameter has a descriptive enum (current, archived, all) and default value. The description only repeats the phrase 'status filter' without adding new parameter-level detail, so it meets but does not exceed the baseline.

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 ('List'), a clear resource ('grinders'), and a scope ('registered for the account'). It also mentions the status filter, making the operation unambiguous and easily distinguishable from sibling list tools for other resources.

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 clearly conveys when to use the tool: to retrieve grinders for the account, with an optional status filter. It does not explicitly name alternatives or exclusions, but for a simple list operation the context is sufficiently clear.

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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
machinesYes

TDQS

A4.5/5.0
Behavior5/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 known. The description adds valuable behavioral nuance: the id is account-relative, counts from 1, is safe to show, and is the identifier other tools expect. This goes beyond the structured fields and prevents an agent from treating the id as a global or sensitive value. No contradiction with 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 sentences, zero wasted words. The primary action and filter are front-loaded in the first sentence, and the id semantics in the second sentence earn their place by preventing misinterpretation. This is an ideal size for a simple list tool.

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 tool is low-complexity (one optional parameter, enums, output schema present), annotations cover safety, and the description addresses the key non-obvious behavior (id semantics). Since an output schema exists, the description does not need to list return fields. 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%: the single status parameter is fully documented with enum values and a default. The description only restates 'with status filter' without adding new parameter-level meaning. According to the baseline rule for high schema coverage, 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?

Description states a specific verb ('List'), resource ('machines'), scope ('registered for the account'), and a modifier ('with status filter'). This clearly differentiates from the many sibling list_* tools (list_beans, list_grinders, etc.) and from mutation tools like register_machine/update_machine. The purpose is unambiguous and actionable.

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 makes the use case clear: an agent should call this when it needs the account's machines, optionally filtered by status. It does not explicitly compare with alternatives or state when not to use it, but for a simple read-only list tool the context is sufficiently clear. Lacks the explicit exclusion/alternative guidance that would earn a 5.

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

list_recipesList locked recipesA
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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
recipesYes

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already carry readOnly, idempotent, and non-destructive hints, and the description adds useful behavior beyond those: each returned id is the account-relative sequence number, safe to display, and is what sibling tools expect as input. This helps an agent understand the contract for chaining calls. No contradiction with 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 sentences, no filler: the first sentence is front-loaded with the call's action and filters, and the second sentence explains the critical id semantics. Every word contributes.

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 need not explain return values, and annotations cover the safety profile. The missing equipment-to-parameter mapping is a gap, but the description's id contract plus the schema make the tool callable and reliable enough.

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 only 25%: status is the only parameter with a definition, while bean_id, grinder_id, and machine_id are bare integer names. The description mentions 'equipment' but never maps it to those three IDs, so it fails to compensate for the low schema 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?

The description opens with a clear verb-resource pair, 'List recipes', and immediately states the scoping options: all or filtered by equipment and status. That level of specificity is enough for an agent to distinguish it from the other list_* siblings without opening their schemas.

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?

No explicit guidance says when to choose list_recipes over alternatives such as list_shots or lock_recipe, and no exclusions or prerequisites are stated. The closest signal is the mention of 'all or filtered by equipment and status,' which only implies a general querying context.

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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
scalesYes

TDQS

A4.3/5.0
Behavior4/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 genuinely useful behavioral context beyond those annotations: the id is an account-relative count starting from 1, safe to display, and is the identifier expected by other tools. No contradictory behavior is described.

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 two concise sentences with no filler. The main purpose is front-loaded, and the second sentence adds a specific, high-value detail about the returned id. Every clause earns its place.

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 one optional, well-documented parameter, an output schema, and strong annotations, this description is complete. It tells the agent what the tool does, what the id means, and how the result should be used. There are no meaningful gaps for an agent to call it correctly.

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, status, already has a clear enum and description in the schema. The tool description does not add any extra parameter-level meaning, but it does not need to because the schema fully documents the filter and its default.

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 opens with a specific verb and resource: 'List registered scales for the account.' It also clarifies the meaning of the returned id, which is exactly what an agent needs to understand what this tool produces. This separates it clearly from non-list sibling tools and gives the action a distinct, concrete outcome.

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 explains that the returned id is 'safe to show' and 'what other tools expect back,' which tells the agent when to use this tool: when it needs a scale identifier to pass into other account-specific operations. It does not explicitly name an alternative or exclusion condition, but the usage context is clear enough for a simple listing 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).

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
shotsYes
totalYes

TDQS

A3.6/5.0
Behavior4/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 covered. The description adds valuable behavioral context: the taste_pending flag is derived (not stored) and explains the exact conditions that trigger it, and it notes that tasting can be backfilled via update_shot. This goes beyond the annotations and helps the agent understand the data semantics.

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, focused sentence that front-loads the core purpose ('List shot history with support for pagination and filtering') and then adds a useful parenthetical clarification about the derived flag. There is no wasted wording, and the structure is efficient.

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 has pagination, filters, and nested objects, and it has an output schema (so return format is covered elsewhere). The description covers the key behavioral nuance (taste_pending) and the backfill path. It doesn't mention default ordering or any other implicit behavior, but given the output schema and annotations, it is sufficiently complete for an agent to call it correctly.

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 67%, so the schema already documents most parameters. The description mentions pagination and filtering generally but does not add details beyond the schema. The taste_pending explanation is repeated from the schema parameter description. Thus the description adds little extra value over 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 clearly states the action (list) and the resource (shot history), and specifies support for pagination and filtering. It is specific enough to distinguish from other list tools, though it doesn't explicitly name any alternative. It doesn't mention sibling differentiators, but the purpose 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 Guidelines2/5

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

No guidance is given on when to use this tool versus alternatives. It doesn't mention any prerequisites or conditions, and it doesn't compare with other listing tools. The description implies it is the listing tool for shots, but it offers no explicit context for selection.

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.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
watersYes

TDQS

A3.9/5.0
Behavior4/5

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

Annotations already cover safety (readOnlyHint), idempotency, and non-destructiveness. The description adds valuable behavioral context by explaining the id semantics: ids count from 1, are safe to display, and are what other tools expect. This goes beyond the schema and 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 sentences with no wasted words. The main action and scope are front-loaded, and the additional id semantics sentence earns its place by providing crucial cross-tool context.

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 optional parameter, full schema coverage, rich annotations, and an output schema, the description is complete. It additionally covers the non-obvious id convention that agents need for downstream tool calls.

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 sole parameter, status, is fully documented in the schema with its enum values and default behavior, so the description does not need to repeat it. The description adds no extra parameter-level meaning, but with 100% schema coverage the baseline of 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 clearly states a specific action and resource: 'List registered waters for the account.' This is distinct from the sibling register_water and list_* tools by resource type, though it does not explicitly name a sibling to differentiate from.

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 makes it clear that this is the tool for listing registered waters, and the account scoping is stated. However, it does not provide explicit guidance on when to use this over list_recipes or other listing siblings, nor does it mention exclusions. 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.

lock_recipeLock a recipeB
Idempotent
Inspect

Lock a successful shot as the reference dialing recipe for this equipment profile.

ParametersJSON Schema
NameRequiredDescriptionDefault
drink_intentYes
from_shot_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already indicate this is a non-read-only, non-destructive, idempotent operation. The description adds useful context that the tool sets a persistent reference recipe for the current equipment profile, but it does not disclose side effects such as whether a previous locked recipe is overwritten. 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, front-loaded sentence with no filler or redundant wording. Every word contributes to the core action and outcome.

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?

The tool has two required parameters and zero schema descriptions, so the description carries a heavy burden. It leaves `drink_intent` unexplained, does not clarify how the equipment profile is resolved, and omits any mention of overwriting behavior. While annotations and output schema help, the description alone is not complete enough for confident invocation.

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%, so the description must compensate, but it does not explain `from_shot_id` or `drink_intent`. It indirectly suggests `from_shot_id` should reference a successful shot, but `drink_intent` (straight vs. milk) is entirely unexplained, leaving the agent without enough meaning to populate the parameter correctly.

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 opens with a specific verb ('Lock') and names the object ('a successful shot') and the outcome ('reference dialing recipe for this equipment profile'). It clearly conveys what the tool does and is distinguishable from siblings like log_shot or update_shot, though it does not explicitly name the distinction.

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 'a successful shot' implies the tool should be used after a shot has been dialed in successfully, which is a useful prerequisite. However, the description does not explicitly state when to use this tool versus alternatives, nor does it mention when not to use it.

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 — so record the offset here if you counted the clicks while reassembling, which is the one moment anybody actually knows it. Read the available tasks, their intervals and their due state from list_grinders.

ParametersJSON Schema
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 in the task catalog on list_grinders; a burr clean takes none
evidenceNoOne line recording what the measurement rested on, e.g. "Counted 6 clicks coarser at reassembly."
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

ParametersJSON Schema
NameRequiredDescription
entryYes
messageYes
entry_idYes

TDQS

A4.2/5.0
Behavior4/5

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

The description adds important behavioral context beyond the sparse annotations, especially the calibration-epoch effect of disassembly and the significance of recording offsets at reassembly. It does not cover every side effect, but it explains the most subtle and consequential behavior of the 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, with the core purpose in the first sentence and the most important caveat immediately following. Every clause earns its place, and it avoids repeating what the schema already documents.

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 tool with 10 parameters and a nested object, the description plus the fully covered schema provide a complete picture. It explains the key domain behavior, points to the correct catalog source, and the output schema removes the need to document return values.

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 the baseline is 3, but the description meaningfully enriches the offset_value and task parameters by explaining their role in the calibration epoch and pointing to list_grinders for the task catalog. Other parameters are already well described in 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 clearly states the tool's action and resource: logging a cleaning or maintenance job against a piece of equipment. It conveys the domain-specific calibration-epoch concept, which helps distinguish it from other logging tools like log_shot, though it does not explicitly contrast itself with those 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?

The description gives concrete guidance on when to record an offset and directs the agent to list_grinders for available tasks, intervals, and due states. It does not explicitly state when not to use this tool or name alternatives such as set_maintenance_offset, but the usage context is clear.

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
leverYes
directionYes
rationaleYes
confidenceYes
cited_rulesNo
based_on_shotNo

Output Schema

ParametersJSON 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.

ParametersJSON Schema
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 — 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. missed the timer — "~25s")
vessel_weight_gNoCup/vessel weight in grams
pressure_profileNoPressure profile description or values
first_drip_time_sNoTime to first drip in seconds

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
shot_idYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare the mutation/safety profile (readOnly=false, idempotent=false, destructive=false), so the description carries additional burden and delivers: how context defaults resolve, that a tag-less/rating-less shot is silently filed taste-pending, and that the diagnosis engine reads tags/metrics rather than flavor_notes. These are real system-state consequences an agent could not infer elsewhere.

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?

Single dense paragraph, front-loaded with the core purpose before the override/taste rules, and nearly every clause carries actionable content. It is long, but the length is justified by the tool's complexity and 32 parameters.

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 an output schema exists (return values needn't be described) and annotations cover the safety profile, the description fills the key gaps an agent needs: context resolution, backdating, and the taste/tag filing rules. Remaining gaps (e.g., explicit auth/permission requirements) are minor.

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 32 params at 66% schema coverage, the description adds genuine semantics beyond the schema for the highest-stakes fields: bean_id override and the wrong-bag risk, pulled_at backdating, sensory_tags for diagnosis, and tasted=1. It does not explain the required dose_g/yield_g/time_s/finish_action, but those are self-evident and schema-documented.

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 (Log) and resource (espresso or alternative shot/brew attempt), which cleanly separates it from siblings update_shot, delete_shot, and list_shots. An agent can identify the operation 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?

Gives explicit when/when-not rules: use active context unless overriding via *_id, ALWAYS pass bean_id when the user names a coffee, backdate with pulled_at for earlier shots, and pass tasted=1 (no tags) for clean tastings. Each rule maps a user situation to a concrete invocation choice.

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).

ParametersJSON Schema
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

ParametersJSON 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?

Annotations already mark this destructive and non-idempotent, but the description goes far beyond them: it enumerates what migrates (bags, shots, recipes, recommendations), the special-case rules for the active coffee, origins moving only when the target has none, and the lock conflict resolution ('the newer lock wins and the older is deleted'). It also clarifies that the source is archived and never deleted, and warns 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?

Two dense sentences, front-loaded with the action and the condition, followed by the behavioral rules. Given the destructive and conflict-sensitive nature of the operation, every clause carries useful information and none is 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 destructive merge with an output schema already covering the return value, the description supplies everything an agent needs: trigger condition, data migration rules, conflict resolution, archive-not-delete reassurance, irreversibility warning, and a verification path via list_beans.

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 all three parameters are already documented, giving a baseline of 3. The description nevertheless adds meaning beyond the schema by stating the direction of data flow ('of from_bean_id moves to into_bean_id') and clarifying the archive semantics of the emptied coffee.

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 situation that triggers it ('the same coffee was registered twice'). An agent can immediately distinguish this from siblings like update_bean or register_coffee 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 Guidelines4/5

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

The triggering condition is explicit ('when the same coffee was registered twice') and it names list_beans as the way to obtain and verify the two IDs, plus a prerequisite to confirm with the user. It does not, however, state when NOT to use it or contrast it against a plausible alternative such as update_bean.

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.

ParametersJSON Schema
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.
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

Output Schema

ParametersJSON Schema
NameRequiredDescription
bag_idYes
statusYes
bean_idYes
messageYes

TDQS

A4.7/5.0
Behavior4/5

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

Annotations cover the safety profile (readOnlyHint=false, destructiveHint=false), so the description credits itself by adding non-obvious behavior: the existing coffee's recipes, notes and history are preserved, only purchase facts are new, the active coffee is untouched, and a bag_id is returned from a separate numbering series. It stops short of stating the practical consequence of idempotentHint=false (repeated calls create separate bags), which would be the natural next disclosure.

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 identity of the tool, then inheritance behavior, then the no-side-effect note, then the routing alternative. No filler or repetition of schema content; every sentence earns its place.

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 not be detailed, yet the description still explains the one surprising part of the return (bag_id belongs to its own series). Together with the routing rule and inheritance semantics, an agent has everything needed to invoke this correctly among the register_*/update_* siblings.

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 62% schema coverage and 13 parameters, the schema already documents required formats and field meanings. The description adds a useful mental model by enumerating which inputs belong to the bag ('roast date, weight, price, state') versus the inherited coffee, and reinforces that bag_notes is about this bag only. It does not clarify the remaining fields like ean, qr_code, storage, or currency.

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 explicitly scopes it to a re-purchase of an existing coffee. It distinguishes itself from the closest sibling, register_coffee, without the agent needing to open either schema, and clarifies that bag numbers are a separate series from bean numbers.

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 ('a coffee that is already in list_beans — the same roaster's product, bought again') and the when-not with the alternative named ('For a coffee that is not in list_beans yet, call register_coffee instead'). It also notes the side effect that it does not change the active coffee, which is a routing-relevant condition.

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, and make it the active coffee. 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.

ParametersJSON Schema
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
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
rest_window_days_maxNo
rest_window_days_minNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
bag_idYes
statusYes
bean_idYes
messageYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations declare the safety profile (readOnlyHint=false, destructiveHint=false, idempotentHint=false), so the bar is lower; the description still adds non-annotation behavior: the side effect of making the new coffee the active one, the duplicate precondition, and the returned identifiers. It does not say what happens on a duplicate name or whether anything is overwritten, which is the remaining gap.

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, front-loaded with the action and precondition, then the domain model that justifies the dual entity, then the alternative and return values. No filler; every clause carries routing or modeling information an agent needs.

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 45-parameter creation tool the description covers the hardest part — the coffee/bag split and the sibling routing — and an output schema exists so return values need not be re-explained. What is missing is duplicate/conflict behavior and coverage of the many non-obvious optional fields, but the essential calling 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 description coverage is 44% and there are 45 parameters, so the schema cannot carry this alone. The description usefully partitions fields into the coffee's attributes versus the bag's purchase attributes, which is genuine meaning beyond field names, but it gives no formats, ranges, or guidance for the large set of undocumented parameters (ean, qr_code, co2e_kg, arabica_pct, decaffeinated, dial_category, etc.).

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 coffee ... together with its first bag') and explicitly distinguishes the coffee entity from the bag entity with field groupings. It also names the sibling it is not (register_bag) and the precondition state (not yet in list_beans), so an agent can tell it apart from update_bean/merge_beans without opening a 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 ('a coffee that is NOT yet in list_beans') and an explicit alternative with the reason to choose it ('for another bag of a coffee already on file call register_bag instead — it keeps the recipes and notes'). This is a real routing rule, not just implied context.

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

register_grinderRegister a grinderBInspect

Register a manual or electric grinder with its dial settings scheme.

ParametersJSON Schema
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
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
grinder_idYes

TDQS

B3.3/5.0
Behavior2/5

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

Annotations are all false and provide no real safety or behavior context, so the description carries the disclosure burden. It only says 'Register,' without explaining persistence effects, uniqueness expectations, validation requirements, or any other runtime 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?

The description is a single front-loaded sentence with no filler. It conveys the core action, the accepted grinder types, and the key settings aspect in minimal words.

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 13-parameter registration tool with an output schema, the description is quite thin. It does not explain registration prerequisites, the relationship to grinder_math or set_grinder_position, or when compound settings/components_spec become relevant.

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 77%, and the description adds only the terms 'manual or electric' and 'dial settings scheme,' which loosely map to motor_type and setting_scheme. It does not add meaning for required but undocumented parameters like burr_type or direction_convention.

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 ('Register') and resource ('grinder'), and adds a meaningful qualifier: 'manual or electric ... dial settings scheme.' This clearly distinguishes it from sibling tools like list_grinders and set_grinder_position.

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 its use when adding a new grinder to the system, but it does not explicitly state when to use it versus alternatives such as list_grinders, set_grinder_position, or grinder_math. No exclusions or selection criteria are provided.

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 machineBInspect

Register an espresso or alternative preparation machine.

ParametersJSON Schema
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.
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
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
machine_idYes

TDQS

B3.2/5.0
Behavior2/5

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

Annotations already indicate this is a non-read-only, non-idempotent write, but the description adds no behavioral detail beyond the word "Register." It does not mention record creation, duplicate behavior, required ownership, or any side effects, so transparency beyond annotations is minimal.

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 clear sentence with no filler. It is front-loaded with the core purpose and does not waste tokens on redundant information.

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?

Given a 12-parameter registration tool with an output schema, the description is thin, but the input schema covers most parameters with useful descriptions such as prep_style and prep_type. Missing context includes when to register a machine versus other equipment and the behavioral implications of creating a non-idempotent record, so the description is only partially complete.

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 67%, and the description itself adds no parameter-level meaning. The phrase "espresso or alternative preparation machine" loosely hints at prep_type, but the schema already documents that. Important fields like control_type and boiler_type remain undocumented in the description, and the tool description does not compensate for the uncovered 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 uses a specific verb and resource: "Register an espresso or alternative preparation machine." It clearly identifies the object being created and distinguishes this tool from siblings like register_grinder and register_program by naming the machine resource directly.

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 guidance on when to use this tool versus alternative registration tools, nor any mention of prerequisites or exclusions. The only signal is the word "machine" in the name and description, leaving the agent to infer applicability.

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 programAInspect

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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
program_idYes

TDQS

A4.2/5.0
Behavior3/5

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

The annotations already indicate this is a write operation (readOnlyHint=false) and not explicitly destructive (destructiveHint=false). The description adds useful domain behavior, such as how programs cut off shots by weight or time and that a machine can mix both program types, but it does not describe side effects like overwriting an existing program or validation failures.

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 three concise sentences with no filler. The main purpose is front-loaded, and the conditional program-type logic is explained efficiently.

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 with an output schema and moderate parameter complexity, the description covers the essential distinctions and the core conditional parameter logic. It does not address every edge case, such as whether registering an existing program_number replaces it, but the provided information is sufficient for most correct invocations.

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 71%, and the schema already documents each parameter. The description adds valuable cross-parameter meaning by linking program_type 'volumetric' to target_volume_g and 'timed' to target_time_s, which helps prevent incorrect combinations. It does not add much about volume_source, but the schema handles that.

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: 'Register a programmed shot button on a machine,' and clarifies the two program types ('volumetric' and 'timed'). This clearly distinguishes it from sibling registration tools like register_machine, register_scale, or register_coffee.

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 gives clear context for when to use the tool: when registering a shot program on a machine, and it explains the two modes supported. It does not explicitly name alternative tools or say when not to use it, but the context is strong enough to route an agent correctly.

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

register_scaleRegister a scaleBInspect

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

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
scale_idYes

TDQS

B3.2/5.0
Behavior2/5

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

Annotations are all negative hints (readOnlyHint=false, idempotentHint=false) and provide no positive behavioral profile. The description adds no side-effect transparency: it does not explain duplicate-name handling, whether registration overwrites, what the response contains, or whether the operation is reversible. For a write operation this is a notable gap.

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 a single front-loaded sentence with no filler and a useful clarifying clause. It is efficient and readable, though it leaves room for more substantive guidance.

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?

The input schema and output schema are comprehensive, so much of the operational detail is already structured. However, given the number of parameters and the sibling toolset, the description itself is thin on when to use it and what registering entails behaviorally. A little more context would make the tool fully self-explanatory.

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 carries rich, actionable descriptions, so the schema does the heavy lifting. The description adds only contextual flavor about what a brewing scale is; no additional parameter semantics are needed.

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 ('Register') and a clear resource ('brewing scale'), and adds domain context by defining the scale as 'the instrument that weighs the dose and the yield.' It is identifiable among siblings by object type, though it does not explicitly distinguish itself from other register_* tools.

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 implied by the name and description: use this when registering a new scale. However, there is no explicit guidance about when to prefer this over list_scales, register_grinder, or other sibling tools, and no mention of prerequisites or exclusions.

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.

ParametersJSON Schema
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

ParametersJSON 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 something already makes it active, so use this to switch back to something that exists, not after register_coffee.

ParametersJSON Schema
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. null = leave unchanged

Output Schema

ParametersJSON 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 convey idempotence, non-read-only, and non-destructive behavior. The description adds useful account scoping and source-list context, but it does not describe failure behavior or side effects beyond switching; with annotations present, this is acceptable 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?

Three short sentences front-load the action and resource, then give the value source and the key exclusion. There is no filler or redundant detail.

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 description plus schema and annotations are mostly complete, especially with an output schema present. A slight gap is that the prose omits the program_id component, though the schema covers it fully.

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 schema descriptions already cover all four parameters at 100% and explain that null means unchanged and values come from list calls. The description reinforces the list-source idea but does not add meaningfully beyond the schema, so the baseline of 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?

The description opens with a specific action and resource: switching the active bean, grinder, or machine for this account. It also distinguishes itself from register_coffee by noting that registration already makes something active, so the agent clearly sees why set_active exists.

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?

It explicitly says the IDs come from list_beans, list_grinders, and list_machines, and it gives a concrete when-not-to-use rule: do not call this after register_coffee. This is strong usage guidance that routes the agent to the correct context.

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.

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

Output Schema

ParametersJSON 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
Idempotent
Inspect

Set the current grinder collar position. source="measured" resets verification freshness.

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

Output Schema

ParametersJSON Schema
NameRequiredDescription
sourceYes
statusYes
messageYes
grinder_idYes
setting_labelYes
position_valueYes

TDQS

A3.6/5.0
Behavior4/5

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

Annotations already indicate this is a mutating but non-destructive, idempotent operation. The description adds a meaningful behavioral consequence beyond the annotations: source='measured' resets verification freshness. This helps the agent understand the side effect of choosing that source value.

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 two short sentences with no filler. The main action is front-loaded, and the additional behavioral note is concise and relevant.

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 three-parameter mutating tool with annotations and an output schema, the description covers the essential semantics. The main missing piece is routing guidance against sibling tools, but that gap is already accounted for in 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 description adds semantic value for 'source' by explaining the freshness-reset behavior, which is not in the schema. However, 'grinder_id' is left undocumented in both schema and description, and 'setting_label' only gains mild context from the phrase 'collar position'. With 67% schema coverage, the description partially compensates but does not fully cover the gaps.

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 clearly states the action ('Set') and the resource ('current grinder collar position'), which directly matches the tool name and title. It is specific enough to distinguish from read-oriented siblings like get_dial_state, but it does not explicitly name or differentiate from sibling tools.

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 versus alternatives, nor does it mention prerequisites or exclusions. The note about source='measured' resetting verification freshness is a behavioral detail, not usage guidance.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
entryYes
messageYes

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already indicate this is a write (readOnlyHint=false) and idempotent (idempotentHint=true). The description adds valuable behavior beyond annotations: zero is a legitimate measurement meaning 'unchanged', and omitting offset_value clears the measurement. It also explains that the offset belongs to the event, enabling composition. No contradiction with 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?

The description is concise, front-loaded with the primary purpose, and efficiently explains the key nuances (entry vs equipment, zero vs omit). It packs several important points into a few sentences without being verbose, though it could be slightly more streamlined.

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 has 4 parameters (1 required), an output schema exists, and annotations cover idempotency and non-read-only behavior, the description is fairly complete. It explains the conceptual background (calibration epoch, composition) and the critical zero/omit distinction. It doesn't cover error cases or prerequisites, but those are not essential for an agent to call it correctly.

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%, so all four parameters are documented in the schema. The description adds a subtle nuance on offset_value (zero means measured and unchanged) and clarifies entry_id's role, but these are largely already in the schema. The description does not add substantial new semantics beyond the schema, so a baseline 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 states the verb 'record' and the specific resource: how far a maintenance job moved the reference, on an entry that opened a calibration epoch. It explicitly distinguishes from equipment-level tools by emphasizing 'Takes an ENTRY, not a piece of equipment,' which differentiates it from siblings like set_grinder_position or update_grinder.

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 gives clear context on when to use the tool: when a maintenance entry opened a calibration epoch, and clarifies that it applies to an entry, not equipment. It also explains how to clear the measurement. However, it does not explicitly name alternative tools for equipment-level offsets, though it implies them.

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
bean_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
categoryYes
warningsYes
next_stepYes

TDQS

B3.1/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 description does not need to restate safety. It adds that the result is a 'single' step and comes 'from the reasoning engine,' which is useful, but it does not disclose anything about error behavior or the meaning of 'next.'

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 definition is a single, front-loaded sentence with no filler. 'Single' and 'reasoning engine' both earn their place by constraining the result.

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 one-parameter, read-only, idempotent tool with an output schema, the description is close to sufficient. However, it lacks any explanation of what a valid bean_id is, what 'next' means relative to, and when this tool should be selected, leaving some context for the agent to infer.

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?

The input schema has one required integer parameter, bean_id, with 0% schema description coverage, and the description never mentions bean_id or explains what values are valid. The name is somewhat self-explanatory, but the description adds no semantic 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 names a concrete operation ('Retrieve ... next experiment step') and a source ('reasoning engine'), so an agent can tell what the tool returns. It does not explicitly differentiate from sibling tools, but none has an obviously similar name.

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?

No guidance is given about when to call this tool versus alternatives such as diagnose_shot or log_recommendation, and no prerequisites or exclusions are mentioned. The intended use must be inferred from the name and one-line description.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare idempotentHint=true, destructiveHint=false and readOnlyHint=false, so the safety profile is largely covered; the description adds the scope of what actually changes. Minor gap: it advertises 'thaw' as an action while the state enum only offers sealed/open/frozen/finished, which an agent must reconcile via unfrozen_at, and it says nothing about permissions or partial-update semantics for the fields object.

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: capability first, sourcing of bag_id second, sibling hand-off last. No filler, and the most load-bearing information (what the tool changes) is front-loaded.

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 annotations covering safety and an output schema present, the description supplies the scope and the alternative-tool routing that the structured fields cannot. It is slightly incomplete about the thaw/state reconciliation, but otherwise sufficient for a 3-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 67% and the nested fields object carries its own per-field descriptions (e.g. grams_remaining, weight_adjustment_g, bag_id 'Never guess one'). The description names a few updatable fields but adds no syntax or meaning beyond the schema, so baseline 3 is appropriate given the schema does the heavy lifting.

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 concrete verb and resource ('Change bag-level facts on one bag of a coffee') and enumerates the actual mutations (open, seal, freeze, thaw, finish, correct roast date/weight/grams left/price). It explicitly distinguishes itself from the sibling update_bean ('For the coffee itself ... use update_bean'), so an agent can route 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 explicit routing: bag-level facts go here, coffee-level facts (roaster, origin, roast level, rest window, notes) go to update_bean. It also tells the agent where bag numbers come from ('from the bags[] on list_beans (or current_bag_id)'). When-to-use and the alternative are both present.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare the safety profile (writable, idempotent, non-destructive), so the description wisely spends its words on behavior those annotations can't convey: the current-bag routing rule and the fact that changing dial_category alters engine advice (G5, C10). It stops short of covering what happens when multiple bags exist and a bag field still arrives.

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?

Front-loaded with the core action, then the routing rule, then the dial_category caveat — three dense sentences with no filler. Each clause carries distinct, actionable 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 annotations cover idempotency and safety. The description handles the tricky routing and the side-effect of dial_category, but for a tool with a large nested `fields` object it could be clearer about what 'CURRENT bag' means when bags proliferate.

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 only 33%, so the description must carry weight, and it does: it splits the 40-plus flat fields into bean-level vs bag-level (state, roast_date, bag_weight_g, grams_remaining, cost) — a categorization the schema itself never states. It adds the dial_category/engine linkage as well, though it can't fully compensate for every undocumented field.

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 the field families it governs (roaster, origins, roast level, rest window, notes, rating). It also names the sibling it is NOT, distinguishing it cleanly from update_bag.

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: bag-shaped fields sent here land on the CURRENT bag, and 'use update_bag when it has more than one' gives the exact condition for choosing the alternative. Nothing about when-to-use is left to inference.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON 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.7/5.0
Behavior5/5

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

The description discloses many non-obvious side effects beyond the annotations: partial-update semantics, null/empty-string clearing rules, maintenance history recording, calibration epochs for locked recipes, current-setting marking/clearing, offset conversion, and direction_convention correction behavior. No contradiction with the annotations was found.

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 dense and information-rich with no filler, and it front-loads the core purpose. However, it is a long wall of text with awkward phrasing such as 'fields may be omitted when only reason is sent' and minor punctuation issues that make it harder to parse quickly.

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 tool's complexity, nested fields, side effects, and existing output schema, the description covers the necessary behavioral context: dry_run guidance, what happens to recorded settings, maintenance history, locked recipes, offsets, and direction correction. An agent has enough information to use the tool responsibly.

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?

Even with 75% schema coverage, the description adds substantial parameter-level meaning: only keys in fields change, null clears optional fields, empty strings clear text but not numbers or closed lists, required fields cannot be cleared, and fields can be omitted when only reason is sent. This goes well beyond the schema's field descriptions.

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 'Correct a registered grinder' and enumerates the editable attributes: name, burrs, collar scheme, nominal step, layout, range, notes. This clearly distinguishes it from create/read/set siblings and uses a specific verb and resource.

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 makes the usage workflow explicit: call with dry_run first, review what would change, and tell the user before saving. It does not explicitly name sibling alternatives or list when not to use update_grinder, but the context is clear enough for an agent to route to it.

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
Idempotent
Inspect

Correct a registered machine — its name, control type, prep type and style, basket, boiler, accessories 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. 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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
machine_idYes
changed_keysYes

TDQS

A3.9/5.0
Behavior1/5

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

The annotations declare idempotentHint true, but the reason field is described as 'appended to this record's own notes as an ordinary line,' which means repeated identical calls with the same reason are not idempotent. This is an annotation contradiction, so the score must be 1 despite the otherwise strong behavioral detail.

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 text is front-loaded and information-dense, with nearly every phrase contributing real value. It loses a point because several distinct rules are crammed into long sentences rather than a more scannable structure.

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 practical calling contract is almost fully specified: editable fields, clearing semantics, optional reason behavior, prep_style consequences, and sibling routing are all covered. It falls short only because the 'appended' reason behavior is left unreconciled with the idempotentHint annotation.

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 description adds material semantics beyond the schema: partial updates, null-to-clear behavior, empty-string handling, required-field clearing rules, and prep_style's effect on diagnosis. It does not elaborate every nested property, but the schema already covers most properties and the description compensates for the remaining gap well.

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 opens with a specific verb and resource ('Correct a registered machine') and enumerates the editable fields. It also names adjacent siblings such as register_program and set_equipment_archived, and explicitly says the grinder link is not editable here, so the tool's purpose 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 Guidelines5/5

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

It gives concrete operational guidance: only sent keys change, null and empty-string clearing semantics, required-field constraints, and when fields may be omitted. It also routes programs to register_program, archiving to set_equipment_archived, and calls out the non-editable grinder link.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
scale_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 goes beyond the annotations by detailing partial-update semantics: only keys sent in fields change, null clears optional fields, empty strings clear only text fields, required fields can be changed but not cleared, and capability fields use special 1/0/null conventions. This is exactly the non-obvious behavioral information an agent needs. It is also consistent with idempotentHint=true and 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 five dense sentences with no filler. The primary action is front-loaded, and each subsequent sentence addresses a specific, actionable nuance about updating or clearing fields. Nothing extraneous is included.

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 nested fields object and nullable enums, the description covers the behaviors that would otherwise require experimentation: partial update, clearing, required-field protection, fields omission, and capability conventions. The output schema exists, so return-value details are not required. No significant calling context 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?

With schema coverage at 67%, the description adds key meanings beyond the schema: the null-vs-empty-string clearing rules, the closed-list caveat, and the 1/0/null semantics for the three capability fields. Field names are largely self-explanatory, so the lack of per-field prose is acceptable. The description compensates for the main ambiguities without documenting every 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?

'Correct a registered scale' names the operation, target, and scope, and the following list ('type, resolution, capacity, capabilities, build or notes') specifies what can be changed. The term 'registered' distinguishes it from register_scale for new scales, though it does not explicitly call out sibling tools.

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 clearly frames the tool as modifying an existing registered scale, which is the main usage context. It also explains when fields may be omitted and how null is used to clear optional values. It does not explicitly name alternatives such as register_scale, but the 'registered' qualifier provides adequate context for an update operation.

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
Idempotent
Inspect

Correct fields on an already-logged shot in place — 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.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsYesOnly the fields to change
shot_idYesID of the shot to correct

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
shot_idYes
updated_fieldsYes

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=false and idempotentHint=true, so safety is covered. The description adds non-obvious derived-value behavior: grind_label re-derives the numeric grind position and yield/time/dose/tds keep flow rate and extraction yield consistent automatically. It stops short of permission or failure-mode disclosure, so not a 5.

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, front-loaded with the core action and the reason to prefer it over delete/re-log, then the concrete use cases and side effects. 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 and annotations covering safety, the description covers the key gap: the in-place correction semantics and the automatic re-derivation side effects. It fully equips an agent to call it, with only minor omission of error/permission behavior.

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 bean_id, bag_id, tasted, data_confidence, etc. The description's parameter references (dose/yield/time, grind_label, bean_id, pulled_at) largely restate schema text, so baseline 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?

States a specific verb and resource ('Correct fields on an already-logged shot in place') and immediately distinguishes itself from delete_shot/log_shot by noting no delete/re-log is needed. An agent can tell it apart from the sibling delete_shot and log_shot without opening a 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?

Gives a concrete list of when-to-use cases (fix wrong dose/yield/time, re-file onto a bean, backfill rating/notes, fix timestamp) and rules out the delete-and-re-log alternative. It does not explicitly name delete_shot or log_shot as alternatives, but the context is unambiguous.

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
Idempotent
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.

ParametersJSON Schema
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

ParametersJSON Schema
NameRequiredDescription
statusYes
messageYes
water_idYes
changed_keysYes

TDQS

A3.9/5.0
Behavior4/5

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

The annotations already declare readOnlyHint=false, destructiveHint=false, and idempotentHint=true. The description adds the partial-update semantics: 'Only the keys you send in fields change' and explains how to clear fields with null or empty strings. It also mentions the behavior of the 'reason' parameter, which is valuable beyond the schema. No contradiction with 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?

The description is a single dense paragraph with key behavior front-loaded ('Only the keys you send change'), and it covers multiple important details without being overly verbose. It could be broken into bullet points for clarity, but the flow is logical.

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 complexity (nested object, partial updates, null clearing) and the existing schema descriptions for water_id and reason, plus annotations covering idempotency, the description covers the essential behavioral rules. The output schema exists, so return value details are not needed. The only small gap is that it doesn't explicitly mention that 'fields' is optional, but that's implied by 'may be omitted' for reason, and the schema doesn't mark it required.

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 67%: water_id and reason have descriptions, but the 'fields' nested object has no overall description. The description clarifies the semantics of 'fields' (partial update, null clearing), which compensates for the schema gap. However, it doesn't provide details on each individual field (gh, kh, tds) beyond names, which are self-explanatory. So the description adds moderate value.

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 clearly states the verb 'Correct' and resource 'a registered water', and lists the modifiable fields (name, type, mineral figures, notes), so an agent understands the tool's core function. However, it doesn't explicitly differentiate from sibling tools like update_bean or update_machine, though the resource is explicit in the title.

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 guidance on when to use this tool relative to others: it corrects an existing water, implying it should be used after listing or registering a water, and mentions list_waters in the schema. It doesn't explicitly state when NOT to use it or name alternatives, but the context is clear enough.

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. 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
  2. 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)"
  3. 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"
  4. 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"
        +}
  5. 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"
        +]
  6. 34 tool updates
    • Changedcompute_age1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "age_days": {
        +      "type": "integer"
        +    },
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "days_off_roast": {
        +      "type": "integer"
        +    },
        +    "days_since_opened": {
        +      "type": "null"
        +    },
        +    "effective_age_days": {
        +      "type": "integer"
        +    },
        +    "frozen_days_excluded": {
        +      "type": "integer"
        +    },
        +    "grams_remaining": {
        +      "type": "null"
        +    },
        +    "rest_window": {
        +      "type": "string"
        +    },
        +    "roast_date": {
        +      "type": "string"
        +    },
        +    "verdict": {
        +      "type": "string"
        +    },
        +    "warnings": {
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "bean_id",
        +    "roast_date",
        +    "age_days",
        +    "days_off_roast",
        +    "days_since_opened",
        +    "frozen_days_excluded",
        +    "effective_age_days",
        +    "rest_window",
        +    "verdict",
        +    "grams_remaining",
        +    "warnings"
        +  ],
        +  "type": "object"
        +}
    • Changeddelete_shot1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "deleted_shot_id": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "deleted_shot_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changeddiagnose_preview1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "bean_context": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "bean_id": {
        +          "type": "integer"
        +        },
        +        "bean_name": {
        +          "type": "string"
        +        },
        +        "dial_category": {
        +          "type": "string"
        +        },
        +        "effective_age_days": {
        +          "type": "integer"
        +        },
        +        "roast_level": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "bean_id",
        +        "bean_name",
        +        "roast_level",
        +        "effective_age_days",
        +        "dial_category"
        +      ],
        +      "type": "object"
        +    },
        +    "category": {
        +      "type": "string"
        +    },
        +    "facts": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "source": {
        +            "type": "string"
        +          },
        +          "text": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "text",
        +          "source"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "hypotheses": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "cause": {
        +            "type": "string"
        +          },
        +          "cited_rules": {
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "confidence": {
        +            "type": "string"
        +          },
        +          "direction": {
        +            "type": "string"
        +          },
        +          "experiment": {
        +            "type": "string"
        +          },
        +          "lever": {
        +            "type": "string"
        +          },
        +          "target_label": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "cause",
        +          "lever",
        +          "direction",
        +          "confidence",
        +          "experiment",
        +          "cited_rules"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "kb_version": {
        +      "type": "string"
        +    },
        +    "one_variable_only": {
        +      "type": "boolean"
        +    },
        +    "warnings": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "category",
        +    "facts",
        +    "hypotheses",
        +    "warnings",
        +    "one_variable_only",
        +    "kb_version",
        +    "bean_context"
        +  ],
        +  "type": "object"
        +}
    • Changeddiagnose_shot1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "bean_context": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "bean_id": {
        +          "type": "integer"
        +        },
        +        "bean_name": {
        +          "type": "string"
        +        },
        +        "dial_category": {
        +          "type": "string"
        +        },
        +        "effective_age_days": {
        +          "type": "integer"
        +        },
        +        "roast_level": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "bean_id",
        +        "bean_name",
        +        "roast_level",
        +        "effective_age_days",
        +        "dial_category"
        +      ],
        +      "type": "object"
        +    },
        +    "category": {
        +      "type": "string"
        +    },
        +    "facts": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "source": {
        +            "type": "string"
        +          },
        +          "text": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "text",
        +          "source"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "hypotheses": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "cause": {
        +            "type": "string"
        +          },
        +          "cited_rules": {
        +            "items": {
        +              "type": "string"
        +            },
        +            "type": "array"
        +          },
        +          "confidence": {
        +            "type": "string"
        +          },
        +          "direction": {
        +            "type": "string"
        +          },
        +          "experiment": {
        +            "type": "string"
        +          },
        +          "lever": {
        +            "type": "string"
        +          },
        +          "target_label": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "cause",
        +          "lever",
        +          "direction",
        +          "confidence",
        +          "experiment",
        +          "cited_rules"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "kb_version": {
        +      "type": "string"
        +    },
        +    "one_variable_only": {
        +      "type": "boolean"
        +    },
        +    "warnings": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "category",
        +    "facts",
        +    "hypotheses",
        +    "warnings",
        +    "one_variable_only",
        +    "kb_version",
        +    "bean_context"
        +  ],
        +  "type": "object"
        +}
    • Changedget_dial_state1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "active_coffee": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "age_days": {
        +          "type": "integer"
        +        },
        +        "age_verdict": {
        +          "type": "string"
        +        },
        +        "archived": {
        +          "type": "integer"
        +        },
        +        "aromatics": {
        +          "type": "null"
        +        },
        +        "attachments": {
        +          "type": "null"
        +        },
        +        "bag_notes": {
        +          "type": "string"
        +        },
        +        "bag_weight_g": {
        +          "type": "null"
        +        },
        +        "bean_mix": {
        +          "type": "null"
        +        },
        +        "best_by_date": {
        +          "type": "null"
        +        },
        +        "buy_date": {
        +          "type": "null"
        +        },
        +        "co2e_kg": {
        +          "type": "null"
        +        },
        +        "cost": {
        +          "type": "null"
        +        },
        +        "cupping_points": {
        +          "type": "null"
        +        },
        +        "currency": {
        +          "type": "null"
        +        },
        +        "days_off_roast": {
        +          "type": "integer"
        +        },
        +        "days_since_opened": {
        +          "type": "null"
        +        },
        +        "decaffeinated": {
        +          "type": "null"
        +        },
        +        "dial_category": {
        +          "type": "string"
        +        },
        +        "ean": {
        +          "type": "null"
        +        },
        +        "effective_age_days": {
        +          "type": "integer"
        +        },
        +        "favourite": {
        +          "type": "null"
        +        },
        +        "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"
        +        },
        +        "is_active": {
        +          "type": "integer"
        +        },
        +        "name": {
        +          "type": "string"
        +        },
        +        "opened_date": {
        +          "type": "null"
        +        },
        +        "origin": {
        +          "type": "null"
        +        },
        +        "process_type": {
        +          "type": "string"
        +        },
        +        "qr_code": {
        +          "type": "null"
        +        },
        +        "rating": {
        +          "type": "null"
        +        },
        +        "rest_window_days_max": {
        +          "type": "null"
        +        },
        +        "rest_window_days_min": {
        +          "type": "null"
        +        },
        +        "roast_custom": {
        +          "type": "null"
        +        },
        +        "roast_date": {
        +          "type": "string"
        +        },
        +        "roast_level": {
        +          "type": "string"
        +        },
        +        "roast_range": {
        +          "type": "null"
        +        },
        +        "roaster": {
        +          "type": "string"
        +        },
        +        "roasting_type": {
        +          "type": "null"
        +        },
        +        "state": {
        +          "type": "string"
        +        },
        +        "storage": {
        +          "type": "null"
        +        },
        +        "unfrozen_at": {
        +          "type": "null"
        +        },
        +        "url": {
        +          "type": "null"
        +        },
        +        "variety": {
        +          "type": "null"
        +        },
        +        "weight_adjustment_g": {
        +          "type": "integer"
        +        }
        +      },
        +      "required": [
        +        "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"
        +      ],
        +      "type": "object"
        +    },
        +    "active_grinder": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "archived": {
        +          "type": "integer"
        +        },
        +        "built_in_machine_id": {
        +          "type": "null"
        +        },
        +        "burr_type": {
        +          "type": "string"
        +        },
        +        "components_spec": {
        +          "type": "string"
        +        },
        +        "direction_convention": {
        +          "type": "string"
        +        },
        +        "id": {
        +          "type": "integer"
        +        },
        +        "is_active": {
        +          "type": "integer"
        +        },
        +        "max_value": {
        +          "type": "null"
        +        },
        +        "microns_per_step": {
        +          "type": "integer"
        +        },
        +        "min_value": {
        +          "type": "null"
        +        },
        +        "motor_type": {
        +          "type": "string"
        +        },
        +        "name": {
        +          "type": "string"
        +        },
        +        "nominal_step": {
        +          "type": "integer"
        +        },
        +        "notes": {
        +          "type": "null"
        +        },
        +        "photo": {
        +          "type": "null"
        +        },
        +        "position_value": {
        +          "type": "integer"
        +        },
        +        "setting_components": {
        +          "type": "string"
        +        },
        +        "setting_label": {
        +          "type": "string"
        +        },
        +        "setting_scheme": {
        +          "type": "string"
        +        },
        +        "source": {
        +          "type": "string"
        +        },
        +        "verified_at": {
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "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"
        +      ],
        +      "type": "object"
        +    },
        +    "active_machine": {
        +      "additionalProperties": true,
        +      "properties": {
        +        "archived": {
        +          "type": "integer"
        +        },
        +        "basket_size_grams": {
        +          "type": "integer"
        +        },
        +        "boiler_type": {
        +          "type": "string"
        +        },
        +        "built_in_grinder_id": {
        +          "type": "null"
        +        },
        +        "connected_device": {
        +          "type": "null"
        +        },
        +        "control_type": {
        +          "type": "string"
        +        },
        +        "id": {
        +          "type": "integer"
        +        },
        +        "is_active": {
        +          "type": "integer"
        +        },
        +        "name": {
        +          "type": "string"
        +        },
        +        "notes": {
        +          "type": "null"
        +        },
        +        "photo": {
        +          "type": "null"
        +        },
        +        "prep_style": {
        +          "type": "null"
        +        },
        +        "prep_type": {
        +          "type": "null"
        +        },
        +        "tools": {
        +          "type": "null"
        +        }
        +      },
        +      "required": [
        +        "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"
        +      ],
        +      "type": "object"
        +    },
        +    "active_program": {
        +      "type": "null"
        +    },
        +    "locked_recipe": {
        +      "type": "null"
        +    },
        +    "recent_recommendations": {
        +      "type": "array"
        +    },
        +    "recent_shots": {
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "active_coffee",
        +    "active_grinder",
        +    "active_machine",
        +    "active_program",
        +    "recent_shots",
        +    "recent_recommendations",
        +    "locked_recipe"
        +  ],
        +  "type": "object"
        +}
    • Changedget_kb_version1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "kb_version": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "kb_version"
        +  ],
        +  "type": "object"
        +}
    • Changedget_rule1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "rule_id": {
        +      "type": "string"
        +    },
        +    "text": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "rule_id",
        +    "text"
        +  ],
        +  "type": "object"
        +}
    • Changedget_stats1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "beans_consumed": {
        +      "type": "integer"
        +    },
        +    "brews": {
        +      "type": "integer"
        +    },
        +    "effective_age": {
        +      "type": "integer"
        +    },
        +    "grams_consumed": {
        +      "type": "integer"
        +    },
        +    "grams_ground": {
        +      "type": "integer"
        +    },
        +    "grams_remaining": {
        +      "type": "integer"
        +    },
        +    "last_bean": {
        +      "type": "null"
        +    },
        +    "last_grind_setting": {
        +      "type": "null"
        +    },
        +    "last_used": {
        +      "type": "null"
        +    }
        +  },
        +  "required": [
        +    "brews"
        +  ],
        +  "type": "object"
        +}
    • Changedgrinder_math1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "burr_gap_travel_microns": {
        +      "type": "integer"
        +    },
        +    "direction": {
        +      "type": "string"
        +    },
        +    "relative_coarseness_note": {
        +      "type": "string"
        +    },
        +    "step_unit": {
        +      "type": "string"
        +    },
        +    "steps": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "direction",
        +    "steps",
        +    "step_unit",
        +    "burr_gap_travel_microns",
        +    "relative_coarseness_note"
        +  ],
        +  "type": "object"
        +}
    • Changedkb_changelog1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "changelog": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "changelog"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_beans1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "beans": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "age_verdict": {
        +            "type": "string"
        +          },
        +          "archived": {
        +            "type": "integer"
        +          },
        +          "bag_notes": {
        +            "type": "string"
        +          },
        +          "bag_weight_g": {
        +            "type": "null"
        +          },
        +          "cost": {
        +            "type": "null"
        +          },
        +          "currency": {
        +            "type": "null"
        +          },
        +          "days_off_roast": {
        +            "type": "integer"
        +          },
        +          "days_since_opened": {
        +            "type": "null"
        +          },
        +          "decaffeinated": {
        +            "type": "null"
        +          },
        +          "dial_category": {
        +            "type": "string"
        +          },
        +          "effective_age_days": {
        +            "type": "integer"
        +          },
        +          "favourite": {
        +            "type": "null"
        +          },
        +          "finished": {
        +            "type": "integer"
        +          },
        +          "frozen_days_excluded": {
        +            "type": "integer"
        +          },
        +          "grams_remaining": {
        +            "type": [
        +              "null",
        +              "integer"
        +            ]
        +          },
        +          "id": {
        +            "type": "integer"
        +          },
        +          "name": {
        +            "type": "string"
        +          },
        +          "opened_date": {
        +            "type": "null"
        +          },
        +          "origins": {
        +            "type": "array"
        +          },
        +          "process_type": {
        +            "type": "string"
        +          },
        +          "rating": {
        +            "type": "null"
        +          },
        +          "rest_window_days_max": {
        +            "type": "null"
        +          },
        +          "rest_window_days_min": {
        +            "type": "null"
        +          },
        +          "roast_date": {
        +            "type": "string"
        +          },
        +          "roast_level": {
        +            "type": "string"
        +          },
        +          "roaster": {
        +            "type": "string"
        +          },
        +          "shots_remaining": {
        +            "type": "integer"
        +          },
        +          "shots_remaining_basis": {
        +            "type": "string"
        +          },
        +          "shots_remaining_dose_g": {
        +            "type": "number"
        +          },
        +          "shots_remaining_sample": {
        +            "type": "null"
        +          },
        +          "state": {
        +            "type": "string"
        +          },
        +          "storage": {
        +            "type": "null"
        +          }
        +        },
        +        "required": [
        +          "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"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "beans"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_grinders1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "grinders": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "archived": {
        +            "type": "integer"
        +          },
        +          "built_in_machine_id": {
        +            "type": "null"
        +          },
        +          "burr_type": {
        +            "type": "string"
        +          },
        +          "components_spec": {
        +            "type": "string"
        +          },
        +          "direction_convention": {
        +            "type": "string"
        +          },
        +          "id": {
        +            "type": "integer"
        +          },
        +          "maintenance": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "calibration": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "epoch_at": {
        +                    "type": "string"
        +                  },
        +                  "epoch_id": {
        +                    "type": "integer"
        +                  },
        +                  "evidence": {
        +                    "type": "null"
        +                  },
        +                  "offset_at": {
        +                    "type": "null"
        +                  },
        +                  "offset_source": {
        +                    "type": "null"
        +                  },
        +                  "offset_unit": {
        +                    "type": "string"
        +                  },
        +                  "offset_value": {
        +                    "type": "null"
        +                  },
        +                  "stale_recipe_count": {
        +                    "type": "integer"
        +                  },
        +                  "state": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "stale_recipe_count",
        +                  "state",
        +                  "epoch_id",
        +                  "epoch_at",
        +                  "offset_value",
        +                  "offset_unit",
        +                  "offset_source",
        +                  "offset_at",
        +                  "evidence"
        +                ],
        +                "type": "object"
        +              },
        +              "due": {
        +                "additionalProperties": true,
        +                "properties": {
        +                  "burr_clean": {
        +                    "additionalProperties": true,
        +                    "properties": {
        +                      "basis": {
        +                        "type": "string"
        +                      },
        +                      "done": {
        +                        "type": "integer"
        +                      },
        +                      "every": {
        +                        "type": "integer"
        +                      },
        +                      "last_at": {
        +                        "type": "string"
        +                      },
        +                      "ratio": {
        +                        "type": "number"
        +                      },
        +                      "remaining": {
        +                        "type": "integer"
        +                      },
        +                      "state": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "state",
        +                      "ratio",
        +                      "basis",
        +                      "every",
        +                      "done",
        +                      "remaining",
        +                      "last_at"
        +                    ],
        +                    "type": "object"
        +                  }
        +                },
        +                "required": [
        +                  "burr_clean"
        +                ],
        +                "type": "object"
        +              },
        +              "entries": {
        +                "items": {
        +                  "additionalProperties": true,
        +                  "properties": {
        +                    "created_at": {
        +                      "type": "string"
        +                    },
        +                    "details": {
        +                      "type": "null"
        +                    },
        +                    "details_known": {
        +                      "type": "boolean"
        +                    },
        +                    "details_version": {
        +                      "type": "integer"
        +                    },
        +                    "disturbs_zero": {
        +                      "type": "integer"
        +                    },
        +                    "equipment_id": {
        +                      "type": "integer"
        +                    },
        +                    "equipment_kind": {
        +                      "type": "string"
        +                    },
        +                    "evidence": {
        +                      "type": [
        +                        "null",
        +                        "string"
        +                      ]
        +                    },
        +                    "id": {
        +                      "type": "integer"
        +                    },
        +                    "note": {
        +                      "type": [
        +                        "string",
        +                        "null"
        +                      ]
        +                    },
        +                    "offset_at": {
        +                      "type": [
        +                        "null",
        +                        "string"
        +                      ]
        +                    },
        +                    "offset_source": {
        +                      "type": [
        +                        "null",
        +                        "string"
        +                      ]
        +                    },
        +                    "offset_value": {
        +                      "type": [
        +                        "null",
        +                        "integer"
        +                      ]
        +                    },
        +                    "performed_at": {
        +                      "type": "string"
        +                    },
        +                    "task": {
        +                      "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",
        +                    "details_known"
        +                  ],
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              },
        +              "sensitivity": {
        +                "type": "null"
        +              },
        +              "tasks": {
        +                "items": {
        +                  "additionalProperties": true,
        +                  "properties": {
        +                    "details_fields": {
        +                      "type": "array"
        +                    },
        +                    "details_version": {
        +                      "type": "integer"
        +                    },
        +                    "disturbs_note": {
        +                      "type": "string"
        +                    },
        +                    "disturbs_zero": {
        +                      "type": "boolean"
        +                    },
        +                    "every": {
        +                      "additionalProperties": true,
        +                      "properties": {
        +                        "days": {
        +                          "type": "integer"
        +                        },
        +                        "grams": {
        +                          "type": "integer"
        +                        },
        +                        "shots": {
        +                          "type": "integer"
        +                        }
        +                      },
        +                      "required": [
        +                        "days",
        +                        "shots",
        +                        "grams"
        +                      ],
        +                      "type": "object"
        +                    },
        +                    "kind": {
        +                      "type": "string"
        +                    },
        +                    "label": {
        +                      "type": "string"
        +                    },
        +                    "offset_integral": {
        +                      "type": "boolean"
        +                    },
        +                    "offset_unit": {
        +                      "type": "string"
        +                    },
        +                    "sub": {
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "type": "string"
        +                    },
        +                    "why": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "value",
        +                    "kind",
        +                    "label",
        +                    "sub",
        +                    "disturbs_zero",
        +                    "every",
        +                    "why",
        +                    "disturbs_note",
        +                    "offset_unit",
        +                    "offset_integral",
        +                    "details_version",
        +                    "details_fields"
        +                  ],
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              }
        +            },
        +            "required": [
        +              "tasks",
        +              "due",
        +              "calibration",
        +              "sensitivity",
        +              "entries"
        +            ],
        +            "type": "object"
        +          },
        +          "max_value": {
        +            "type": "null"
        +          },
        +          "microns_per_step": {
        +            "type": "integer"
        +          },
        +          "min_value": {
        +            "type": "null"
        +          },
        +          "motor_type": {
        +            "type": "string"
        +          },
        +          "name": {
        +            "type": "string"
        +          },
        +          "nominal_step": {
        +            "type": "integer"
        +          },
        +          "notes": {
        +            "type": "null"
        +          },
        +          "position_value": {
        +            "type": "integer"
        +          },
        +          "setting_components": {
        +            "type": "string"
        +          },
        +          "setting_label": {
        +            "type": "string"
        +          },
        +          "setting_scheme": {
        +            "type": "string"
        +          },
        +          "source": {
        +            "type": "string"
        +          },
        +          "verified_at": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "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"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "grinders"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_machines1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "machines": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "archived": {
        +            "type": "integer"
        +          },
        +          "basket_size_grams": {
        +            "type": "integer"
        +          },
        +          "boiler_type": {
        +            "type": "string"
        +          },
        +          "built_in_grinder_id": {
        +            "type": "null"
        +          },
        +          "control_type": {
        +            "type": "string"
        +          },
        +          "id": {
        +            "type": "integer"
        +          },
        +          "name": {
        +            "type": "string"
        +          },
        +          "notes": {
        +            "type": "null"
        +          },
        +          "prep_style": {
        +            "type": "null"
        +          },
        +          "prep_type": {
        +            "type": "null"
        +          },
        +          "programs": {
        +            "items": {
        +              "additionalProperties": true,
        +              "properties": {
        +                "description": {
        +                  "type": "string"
        +                },
        +                "id": {
        +                  "type": "integer"
        +                },
        +                "is_active": {
        +                  "type": "integer"
        +                },
        +                "program_number": {
        +                  "type": "integer"
        +                },
        +                "program_type": {
        +                  "type": "string"
        +                },
        +                "target_time_s": {
        +                  "type": "null"
        +                },
        +                "target_volume_g": {
        +                  "type": "integer"
        +                },
        +                "volume_source": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "id",
        +                "program_number",
        +                "program_type",
        +                "target_volume_g",
        +                "target_time_s",
        +                "volume_source",
        +                "description",
        +                "is_active"
        +              ],
        +              "type": "object"
        +            },
        +            "type": "array"
        +          },
        +          "tools_list": {
        +            "type": "array"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "name",
        +          "control_type",
        +          "basket_size_grams",
        +          "boiler_type",
        +          "notes",
        +          "prep_type",
        +          "prep_style",
        +          "archived",
        +          "built_in_grinder_id",
        +          "tools_list",
        +          "programs"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "machines"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_recipes1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "recipes": {
        +      "items": {
        +        "additionalProperties": true,
        +        "properties": {
        +          "bean_id": {
        +            "type": "integer"
        +          },
        +          "bean_name": {
        +            "type": "string"
        +          },
        +          "dose_g": {
        +            "type": "number"
        +          },
        +          "drink_intent": {
        +            "type": "string"
        +          },
        +          "finish_action": {
        +            "type": "string"
        +          },
        +          "grind_label": {
        +            "type": "string"
        +          },
        +          "grind_position_value": {
        +            "type": "integer"
        +          },
        +          "grind_reading": {
        +            "additionalProperties": true,
        +            "properties": {
        +              "epochs_since": {
        +                "type": "integer"
        +              },
        +              "label": {
        +                "type": "null"
        +              },
        +              "offset_clicks": {
        +                "type": "null"
        +              },
        +              "offset_direction": {
        +                "type": "null"
        +              },
        +              "unmeasured": {
        +                "type": "integer"
        +              }
        +            },
        +            "required": [
        +              "label",
        +              "offset_clicks",
        +              "offset_direction",
        +              "epochs_since",
        +              "unmeasured"
        +            ],
        +            "type": "object"
        +          },
        +          "grinder_id": {
        +            "type": "integer"
        +          },
        +          "grinder_name": {
        +            "type": "string"
        +          },
        +          "id": {
        +            "type": "integer"
        +          },
        +          "locked_at": {
        +            "type": "string"
        +          },
        +          "machine_id": {
        +            "type": "integer"
        +          },
        +          "machine_name": {
        +            "type": "string"
        +          },
        +          "profile": {
        +            "type": "string"
        +          },
        +          "program_id": {
        +            "type": "integer"
        +          },
        +          "status": {
        +            "type": "string"
        +          },
        +          "target_time_s": {
        +            "type": "integer"
        +          },
        +          "target_yield_g": {
        +            "type": [
        +              "number",
        +              "integer"
        +            ]
        +          }
        +        },
        +        "required": [
        +          "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"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "recipes"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_scales1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "scales": {
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "scales"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_shots1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "shots": {
        +      "type": "array"
        +    },
        +    "total": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "shots",
        +    "total"
        +  ],
        +  "type": "object"
        +}
    • Changedlist_waters1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "waters": {
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "waters"
        +  ],
        +  "type": "object"
        +}
    • Changedlock_recipe1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Addedlog_maintenance
    • Changedlog_recommendation1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedlog_shot1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "shot_id": {
        +      "type": "integer"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "shot_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_coffee7 fields changed
      • changedInput schema / properties / best_by_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / buy_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / frozen_at / description
        Previous value: -"Datetime in YYYY-MM-DD HH:MM:SS format"New value: +"Calendar day the bag went into the freezer, YYYY-MM-DD — a day, not a timestamp"
      • addedInput schema / properties / opened_date / description
        Added value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / roast_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / unfrozen_at / description
        Previous value: -"Datetime in YYYY-MM-DD HH:MM:SS format"New value: +"Calendar day the bag came out, YYYY-MM-DD — a day, not a timestamp"
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "bean_id": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "bean_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_grinder1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "grinder_id": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "grinder_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_machine3 fields changed
      • changedInput schema / properties / prep_style / description
        Previous value: -"Preparation style category, e.g., espresso, filter, immersion"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."
      • changedInput schema / properties / prep_type / description
        Previous value: -"Preparation equipment type, e.g., espresso_machine, v60_dripper, French_press"New value: +"Preparation equipment type. One of: espresso_machine, lever_press, moka_pot, aeropress, french_press, v60_dripper, cold_brew. Lower snake_case."
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "machine_id": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "machine_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_program1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "program_id": {
        +      "type": "integer"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "program_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_scale1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "scale_id": {
        +      "type": "integer"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "scale_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedregister_water1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "water_id": {
        +      "type": "integer"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "water_id",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedset_active1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedset_equipment_archived1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedset_grinder_position1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "grinder_id": {
        +      "type": "integer"
        +    },
        +    "message": {
        +      "type": "string"
        +    },
        +    "position_value": {
        +      "type": "integer"
        +    },
        +    "setting_label": {
        +      "type": "string"
        +    },
        +    "source": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "grinder_id",
        +    "setting_label",
        +    "position_value",
        +    "source",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Addedset_maintenance_offset
    • Changedsuggest_next_step1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "category": {
        +      "type": "string"
        +    },
        +    "next_step": {
        +      "type": "string"
        +    },
        +    "warnings": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "next_step",
        +    "category",
        +    "warnings"
        +  ],
        +  "type": "object"
        +}
    • Changedupdate_bean9 fields changed
      • changedInput schema / properties / fields / properties / best_by_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / fields / properties / buy_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / fields / properties / frozen_at / description
        Previous value: -"Datetime in YYYY-MM-DD HH:MM:SS format"New value: +"Calendar day the bag went into the freezer, YYYY-MM-DD — a day, not a timestamp"
      • addedInput schema / properties / fields / properties / opened_date / description
        Added value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / fields / properties / roast_date / description
        Previous value: -"Date in YYYY-MM-DD format"New value: +"Calendar day in YYYY-MM-DD format — a day, not a timestamp"
      • changedInput schema / properties / fields / properties / unfrozen_at / description
        Previous value: -"Datetime in YYYY-MM-DD HH:MM:SS format"New value: +"Calendar day the bag came out, YYYY-MM-DD — a day, not a timestamp"
      • changedInput schema / properties / reason / description
        Previous value: -"A clear justification why this category/roast level is updated."New value: +"Optional free-text note, appended to the bag notes as an ordinary line. Omit it and nothing is written."
      • changedInput schema / required
        Previous value: -[
        -  "bean_id",
        -  "fields",
        -  "reason"
        -]New value: +[
        +  "bean_id",
        +  "fields"
        +]
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "status": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "message"
        +  ],
        +  "type": "object"
        +}
    • Changedupdate_shot1 field changed
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "additionalProperties": true,
        +  "properties": {
        +    "message": {
        +      "type": "string"
        +    },
        +    "shot_id": {
        +      "type": "integer"
        +    },
        +    "status": {
        +      "type": "string"
        +    },
        +    "updated_fields": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "status",
        +    "shot_id",
        +    "updated_fields",
        +    "message"
        +  ],
        +  "type": "object"
        +}
  7. 3 tool updates
    • Addedlist_scales
    • Addedregister_scale
    • Changedset_equipment_archived1 field changed
      • changedInput schema / properties / kind / enum
        Previous value: -[
        -  "grinder",
        -  "machine",
        -  "water"
        -]New value: +[
        +  "grinder",
        +  "machine",
        +  "water",
        +  "scale"
        +]
  8. 1 tool update
    • Changedregister_machine1 field changed
      • addedInput schema / properties / built_in_grinder_id
        Added value: +{
        +  "description": "id of a grinder built into this machine (bean-to-cup / integrated grinder)",
        +  "type": "integer"
        +}
  9. 10 tool updates
    • Changeddiagnose_preview4 fields changed
      • changedInput schema / properties / sensory_tags / description
        Previous value: -"Sensory observations, e.g. [\"sour\",\"bitter\"]"New value: +"Sensory observations, e.g. [\"sour\",\"bitter\"]. With shot_id: replaces the stored tags for this preview (omit to use stored; [] = none)."
      • addedInput schema / properties / shot_id
        Added value: +{
        +  "description": "Preview a logged shot by ID (dry-run; ignores the metric params below)",
        +  "type": "integer"
        +}
      • changedInput schema / properties / source / description
        Previous value: -"Grinder position source — affects G1 warning"New value: +"Grinder position source — affects G1 warning (raw mode only)"
      • removedInput schema / required
        Removed value: -[
        -  "bean_id",
        -  "grinder_id",
        -  "machine_id",
        -  "grind_label",
        -  "dose_g",
        -  "yield_g",
        -  "time_s",
        -  "source"
        -]
    • Changedlist_shots1 field changed
      • addedInput schema / properties / filters / properties / taste_pending
        Added value: +{
        +  "description": "1 = shots logged without sensory tags, a rating, or the tasted flip (tasting still owed); 0 = tasted shots",
        +  "enum": [
        +    0,
        +    1
        +  ],
        +  "type": "integer"
        +}
    • Changedlog_shot13 fields changed
      • changedInput schema / properties / bean_id / description
        Previous value: -"Override: log against this bean instead of the active one"New value: +"Override: log against this bean instead of the active one (null = active)"
      • changedInput schema / properties / bean_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedInput schema / properties / data_confidence / description
        Added value: +"measured = weighed/timed live; recalled = from memory; estimated = a best guess (e.g. missed the timer — \"~25s\")"
      • changedInput schema / properties / data_confidence / enum
        Previous value: -[
        -  "measured",
        -  "recalled"
        -]New value: +[
        +  "measured",
        +  "recalled",
        +  "estimated"
        +]
      • changedInput schema / properties / grinder_id / description
        Previous value: -"Override: grinder used, if not the active one"New value: +"Override: grinder used, if not the active one (null = active)"
      • changedInput schema / properties / grinder_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedInput schema / properties / machine_id / description
        Previous value: -"Override: machine used, if not the active one"New value: +"Override: machine used, if not the active one (null = active)"
      • changedInput schema / properties / machine_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • changedInput schema / properties / program_id / description
        Previous value: -"Override: machine program used, if not the active one"New value: +"Override: machine program used, if not the active one (null = active)"
      • changedInput schema / properties / program_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • 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. 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 — the default diagnosis target is the chronologically latest shot."
      • changedInput schema / properties / pulled_at / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / tasted
        Added value: +{
        +  "description": "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.",
        +  "enum": [
        +    0,
        +    1
        +  ],
        +  "type": "integer"
        +}
    • Changedregister_grinder3 fields changed
      • changedInput schema / properties / max_value / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / microns_per_step / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / min_value / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
    • Changedregister_machine1 field changed
      • changedInput schema / properties / tools / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
    • Changedregister_program3 fields changed
      • changedInput schema / properties / description / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • changedInput schema / properties / target_time_s / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / target_volume_g / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
    • Changedregister_water6 fields changed
      • changedInput schema / properties / calcium / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / gh / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / kh / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / magnesium / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / sodium / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
      • changedInput schema / properties / tds / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
    • Changedset_active8 fields changed
      • addedInput schema / properties / bean_id / description
        Added value: +"null = leave unchanged"
      • changedInput schema / properties / bean_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedInput schema / properties / grinder_id / description
        Added value: +"null = leave unchanged"
      • changedInput schema / properties / grinder_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedInput schema / properties / machine_id / description
        Added value: +"null = leave unchanged"
      • changedInput schema / properties / machine_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedInput schema / properties / program_id / description
        Added value: +"null = leave unchanged"
      • changedInput schema / properties / program_id / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
    • Changedupdate_bean1 field changed
      • addedInput schema / properties / fields / properties / grams_remaining
        Added value: +{
        +  "description": "Override the computed grams left in the bag (weigh the bag, set what is actually there). Translated into weight_adjustment_g so bag_weight_g keeps recording the true bag size. 0 marks the bag finished; a positive value on a finished bag un-finishes it.",
        +  "type": "number"
        +}
    • Changedupdate_shot9 fields changed
      • addedInput schema / properties / fields / properties / data_confidence / description
        Added value: +"estimated marks the metrics as a best guess instead of burying the caveat in notes"
      • changedInput schema / properties / fields / properties / data_confidence / enum
        Previous value: -[
        -  "measured",
        -  "recalled"
        -]New value: +[
        +  "measured",
        +  "recalled",
        +  "estimated"
        +]
      • addedInput schema / properties / fields / properties / flavor_notes / description
        Added value: +"null clears the notes"
      • changedInput schema / properties / fields / properties / flavor_notes / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
      • addedInput schema / properties / fields / properties / rating / description
        Added value: +"1-5; null clears the rating"
      • changedInput schema / properties / fields / properties / rating / type
        Previous value: -"integer"New value: +[
        +  "integer",
        +  "null"
        +]
      • addedInput schema / properties / fields / properties / tasted
        Added value: +{
        +  "description": "Backfill 1 to mark a pending shot as tasted-clean (tasted, no defect tags) so it leaves the taste-pending queue without inventing a tag or rating",
        +  "enum": [
        +    0,
        +    1
        +  ],
        +  "type": "integer"
        +}
      • addedInput schema / properties / fields / properties / verdict / description
        Added value: +"null clears the verdict"
      • changedInput schema / properties / fields / properties / verdict / type
        Previous value: -"string"New value: +[
        +  "string",
        +  "null"
        +]
  10. 4 tool updates
    • Changedlog_shot5 fields changed
      • addedInput schema / properties / bean_id
        Added value: +{
        +  "description": "Override: log against this bean instead of the active one",
        +  "type": "integer"
        +}
      • addedInput schema / properties / grinder_id
        Added value: +{
        +  "description": "Override: grinder used, if not the active one",
        +  "type": "integer"
        +}
      • addedInput schema / properties / machine_id
        Added value: +{
        +  "description": "Override: machine used, if not the active one",
        +  "type": "integer"
        +}
      • addedInput schema / properties / program_id
        Added value: +{
        +  "description": "Override: machine program used, if not the active one",
        +  "type": "integer"
        +}
      • addedInput schema / properties / pulled_at
        Added value: +{
        +  "description": "When the shot was actually pulled (ISO 8601 or \"YYYY-MM-DD HH:MM:SS\"), for backdated entries. Defaults to now. When backdating, pass the returned shot_id to diagnose_shot explicitly — the default diagnosis target is the chronologically latest shot.",
        +  "type": "string"
        +}
    • Changedregister_coffee2 fields changed
      • addedInput schema / properties / dial_category / description
        Added value: +"Dialing behavior class: classic = medium/traditional espresso roasts (chocolate/nut, balanced); dark = genuinely dark/roasty; the light categories cover Nordic and ultra-light styles."
      • changedInput schema / properties / dial_category / enum
        Previous value: -[
        -  "nordic-light",
        -  "ultra-light",
        -  "processed",
        -  "dark",
        -  "aged"
        -]New value: +[
        +  "nordic-light",
        +  "ultra-light",
        +  "classic",
        +  "processed",
        +  "dark",
        +  "aged"
        +]
    • Changedupdate_bean3 fields changed
      • addedInput schema / properties / fields / properties / dial_category / description
        Added value: +"Dialing behavior class: classic = medium/traditional espresso roasts (chocolate/nut, balanced); dark = genuinely dark/roasty; the light categories cover Nordic and ultra-light styles."
      • changedInput schema / properties / fields / properties / dial_category / enum
        Previous value: -[
        -  "nordic-light",
        -  "ultra-light",
        -  "processed",
        -  "dark",
        -  "aged"
        -]New value: +[
        +  "nordic-light",
        +  "ultra-light",
        +  "classic",
        +  "processed",
        +  "dark",
        +  "aged"
        +]
      • addedInput schema / properties / fields / properties / weight_adjustment_g
        Added value: +{
        +  "description": "Grams consumed or lost OUTSIDE logged shots (spillage, unlogged brews). grams_remaining = bag_weight_g − logged doses − this. To force remaining to a known value X, set this to bag_weight_g − logged doses − X. Negative adds weight back.",
        +  "type": "number"
        +}
    • Addedupdate_shot
  11. 1 tool update
    • Changedregister_program5 fields changed
      • changedInput schema / properties / program_number / description
        Previous value: -"Program number (e.g. 1 or 2)"New value: +"Program number / button index (e.g. 1 or 2)"
      • addedInput schema / properties / program_type
        Added value: +{
        +  "description": "Cutoff method: \"volumetric\" (weight) or \"timed\" (duration). Defaults to volumetric.",
        +  "enum": [
        +    "volumetric",
        +    "timed"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / target_time_s
        Added value: +{
        +  "description": "Target shot duration cutoff in seconds (required for timed programs)",
        +  "type": "number"
        +}
      • changedInput schema / properties / target_volume_g / description
        Previous value: -"Target yield cutoff in grams"New value: +"Target yield cutoff in grams (required for volumetric programs)"
      • addedInput schema / properties / volume_source / description
        Added value: +"Confidence in the stored cutoff value, for either type"
  12. 29 tool updates
    • First observedcompute_age
    • First observeddelete_shot
    • First observeddiagnose_preview
    • First observeddiagnose_shot
    • First observedget_dial_state
    • First observedget_kb_version
    • First observedget_rule
    • First observedget_stats
    • First observedgrinder_math
    • First observedkb_changelog
    • First observedlist_beans
    • First observedlist_grinders
    • First observedlist_machines
    • First observedlist_recipes
    • First observedlist_shots
    • First observedlist_waters
    • First observedlock_recipe
    • First observedlog_recommendation
    • First observedlog_shot
    • First observedregister_coffee
    • First observedregister_grinder
    • First observedregister_machine
    • First observedregister_program
    • First observedregister_water
    • First observedset_active
    • First observedset_equipment_archived
    • First observedset_grinder_position
    • First observedsuggest_next_step
    • First observedupdate_bean

Related MCP Servers

  • 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
    7 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables tracking competitor websites, changelogs, blog feeds, and pricing pages with meaningful diffs, classification, and Markdown digests via MCP tools for listing, adding, removing competitors, running checks, and retrieving digests or changes.
    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