Skip to main content
Glama
tillbooks

tillbooks

Official

asset_depreciation_run_create

Idempotent

Creates a draft depreciation run for a period, calculating expense for eligible assets and saving per-asset lines for bookkeeper review before posting.

Instructions

Create a DRAFT depreciation run for a period (H04): calculate the depreciation for every eligible asset via the H03 engine and persist a run header (status=draft) plus one line per asset that returned amount > 0, so a bookkeeper can review the proposed expense before anything hits the books. Eligible = status active/fully_depreciated, method != none, accumulated depreciation below cost minus residual, last_depreciation_period null or before the target period, not disposed. Optional filters narrow the set: assetIds (an explicit subset), categoryId, costCenterId. unitsByAsset maps assetId to the WHOLE units produced this period and is what makes a units_of_production asset depreciable at all (TILL captures no production data of its own, so the run carries it; same shape as asset_depreciation_preview, and values must be non-negative safe integers up to 1e12 or the create is invalid_input). skipped[] reports assets that produced NO line, as {assetId, assetNumber, reason}: on an EXPLICIT assetIds list every named asset that produced no line is reported (non_depreciable | asset_terminal | already_at_residual | period_already_processed | filtered_out | missing_production_data | missing_units_estimate | zero_amount), because naming an asset is an instruction about that asset; on a SWEEP the eligible register IS the selection, so only assets that reached the calculation are reported (missing_production_data | missing_units_estimate | zero_amount). A units_of_production asset named EXPLICITLY in assetIds is refused, not merely reported, when the input can be corrected: missing_production_data (no figure supplied) or missing_units_estimate (the asset carries no totalEstimatedUnits, so there is no denominator to allocate over). Nothing is written in either case. postingGranularity is detailed (one expense + one accum line per asset, the default) or summarised (grouped by account + cost centre); the per-asset lines exist either way for the sub-ledger audit. period is YYYY-MM. Refused with invalid_period, period_locked (the period is hard-locked in A03) or run_already_exists (a draft for the same period and selection already exists, and the response names it). Writes NO journal (the post verb does). Idempotent on idempotencyKey and on the (period, selection) signature, where the signature is built from the LINES the run would write (asset, amount and production figure), so two creates over the same eligible set yield ONE draft however the filters or the units map were spelled. When there is nothing to charge, NO run is persisted at all: the answer is {ok:true, run:null, lines:[], empty:true, skipped:[...]}, computed fresh, so it is identical on every call however many times it is repeated and whatever key each call carries. A run header exists only where at least one asset is charged, which is why a finished period never leaves an outstanding draft in run_list for a period-close checklist to trip over. Once a period is POSTED its assets carry it as their last depreciation period, so nothing is eligible and a later create for that period returns exactly that empty answer, carrying alreadyPostedRunId: the id of the most recently posted run for the period (two disjoint selections can both post for one period). A foreign assetId is not_found (§H-TENANT).

Input Schema

NameRequiredDescriptionDefault
periodYes
assetIdsNo
categoryIdNo
workspaceIdYes
costCenterIdNo
unitsByAssetNo
idempotencyKeyYes
postingGranularityNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.1.0

TDQS

A4.7/5.0
Behavior5/5

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

Far beyond the annotations: it discloses exactly what is persisted (run header + one line per positive-amount asset), that nothing is written on refusal or on an empty set, the full skipped[] reason taxonomy and its different semantics for explicit assetIds vs sweeps, and the idempotency signature built from the would-be lines. This is consistent with idempotentHint=true and destructiveHint=false rather than contradicting them.

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

Conciseness3/5

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

Front-loaded correctly — the create/calculate/persist core is in the first sentence — but the rest is a single ~450-word paragraph dense with parenthetical codes and nested conditionals that is hard to scan. Nearly all content is substantive, yet some asides (the period-close checklist rationale for no empty draft) could be trimmed or split into structure.

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?

No output schema exists, and the description compensates by specifying the response shape ({ok, run, lines, empty, skipped, alreadyPostedRunId}) and every refusal code (invalid_period, period_locked, run_already_exists, not_found, invalid_input). For an 8-parameter mutation with a nested unitsByAsset object, nothing an agent needs to call it correctly is missing.

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

Parameters4/5

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

Schema coverage is 0%, so the description carries the load and mostly does: period format (YYYY-MM), assetIds as an explicit subset, categoryId/costCenterId as narrowing filters, unitsByAsset as assetId→whole units with a 1e12 non-negative safe-integer bound, postingGranularity's two modes, and idempotencyKey's role. Only workspaceId goes unexplained apart from the tenant hint on foreign assetId.

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

Purpose5/5

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

Opens with a specific verb+resource+outcome: 'Create a DRAFT depreciation run for a period (H04): calculate the depreciation for every eligible asset via the H03 engine and persist a run header (status=draft) plus one line per asset'. It also separates itself from siblings by naming the post verb ('Writes NO journal (the post verb does)') and by referencing the preview's shared shape.

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 eligibility predicate (active/fully_depreciated, method != none, NBV above residual, last_depreciation_period null or earlier, not disposed), names the alternative verb for posting, and states when NOT to expect a result (already-posted period returns the empty answer with alreadyPostedRunId). An agent can decide create-vs-post-vs-preview without opening another schema.

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

Deploy Server

Other Tools