The whole Track so far insisted on the input contract: typed parameters, enums, format, required. But the tool also returns something, and what it returns is a contract of equal weight. The bad part is that most tool authors return "whatever the query spits out" or "a nice sentence" and forget.
The problem: the agent reads the tool's output as context to decide the next step. If that context is ambiguous, the next step is ambiguous. If it's structured, the next step almost programs itself.
Structure always. Return a JSON object with named fields. Never prose when there's discrete data.
Document the shape in the description. The model reads the description before invoking; if it knows what it'll get back, it plans the next step better.
Description: "...Returns: { shift_id, status: 'scheduled'|'active'|
'completed', crewmates: [...], lead_alias }"Explicit enums in outputs too. If status can only be 4 values, say so. The agent will trust and reason over that closed list instead of inventing branches for "active_with_warnings".
When a field might not be there:
null, don't omit the field. notes: null tells the agent "I checked and there's nothing". If you omit notes, the agent doesn't know whether you didn't check, the tool partial-failed, or there's an actual emptiness.nulls in the description. notes: string|null.Errors are output too. You already saw in step 14 that the error shape is:
{ ok: false, error: { code: "...", message: "...", ...extra } }That shape is a contract just like the success shape. Document it. The agent reads it and learns to handle each code.
Mental test: if you were the agent and only saw the raw tool output (no extra context), could you decide the next step with certainty? If the answer is "depends on how I interpret this sentence", the output is poorly designed.