A tool's Python function and the message the model reads back are not the same object, even though it is tempting to write `return result` and let whatever `result` is fall straight into the tool_result content. Anthropic's Messages API expects the result of a tool call back as a content block of the form `{"type": "tool_result", "tool_use_id": ..., "content": ...}` inside a user-role message, with `"is_error": true` set when the call failed. Nothing about that contract says the content has to be your function's raw return value — it says the content has to be something the model can read and act on.
Returning the raw value is a decision, not a default, and it is usually the wrong one. An ORM object, a pandas DataFrame, or a raw upstream API response all carry structure that exists for your code's convenience — foreign keys, pagination envelopes, internal flags — none of which answers the question the model actually asked. Treating "what my function returns" and "what the model needs to see" as interchangeable is the single most common root cause of bloated, confusing agent context, and it compounds because every unshaped result gets re-sent on every later turn of the loop.
This lesson is about the gap between those two objects, and the concrete techniques for closing it: projecting to needed fields, flattening nesting, resolving ids to names, dropping nulls, summarising long collections, choosing a format the model can actually read, and making every result say what it is — including the results nobody wants to think about, like empty and truncated ones.