Tools for a Reader That Isn't Human
An argument for agent-first tooling — high-context, typed, and delivered in JSON
Above all else show the data.

A loupe resolves what the naked eye glosses over. The same data is there either way; the difference is in what the instrument is built to reveal.
For forty years
we have built command-line tools for a particular reader: a person, sitting at a terminal, scanning columns of text with their eyes. ls aligns its output into tidy columns. find prints one path per line. stat lays out a little form. Every one of these decisions is an act of typography — a choice about how to arrange characters so that a human can take them in at a glance.This is not a criticism of those tools. They are superb at what they were designed for. The point is narrower: they were designed for eyes, and we are increasingly asking machines to read them.
That reader is changing. More and more, the thing on the other end of the pipe is not a person but an agent: a language model orchestrating shell commands, reading their output, and deciding what to do next. And an agent reads nothing like a person. It does not scan columns. It does not benefit from alignment. When it receives the output of ls -l, it has to reverse-engineer the typography we so carefully constructed — splitting on whitespace that may or may not be significant, guessing which column is which, hoping the locale hasn’t moved the date format, praying a filename doesn’t contain a space.
We have, in other words, given our most capable new collaborator a stack of documents written in a language it has to decode by hand, every single time. This essay is an argument that we can do much better, and that doing better is not exotic — it is mostly a matter of deciding who the reader is before we decide how to write.
§ I · The contractOutput for eyes, output for agents
Consider what actually happens when an agent calls ls -l and tries to act on the result. The text was rendered for display, so the agent must un-render it. It writes a regular expression, or splits on spaces, and extracts what it hopes are the right fields.And it does this probabilistically. A model parsing whitespace-aligned columns is doing statistical pattern-matching on a format that was never meant to be a format. It usually works, which is worse than if it never worked — because the failures are rare, silent, and load-bearing.
This works until a directory has a file named My Report (final).pdf, or the system locale prints juin instead of Jun, or someone passes -h and 4096 becomes 4.0K. The parsing breaks, and it breaks downstream, far from the cause.
The alternative is to decide that the agent is a first-class reader and to write for it directly. Not formatted text that happens to be machine-adjacent, but a typed contract: every field named, every value typed, the whole thing self-describing.
The difference looks small on the page and is enormous in practice. On the left, correctness depends on a chain of fragile assumptions about formatting. On the right, correctness depends on nothing but the field names, which are part of a published contract. The agent does not parse; it accesses.
§ II · What "agent-first" meansFour properties of a tool an agent can trust
If we take the agent seriously as a reader, a handful of properties stop being nice-to-haves and become the whole point. They are not difficult. They are mostly a refusal to make the agent do work that the tool could have done once, correctly, on its behalf.
It is typed.
A size is a number, not the string 4.0K. A timestamp is a Unix integer, not a locale-formatted date that means different things on different machines.Typing is the single highest-leverage decision. Once a value has a type, an enormous category of parsing bugs simply cannot occur — there is no string to misread.
A type is a promise the tool keeps so the reader doesn’t have to verify it.
It is self-describing.
The output conforms to a schema, and the schema is retrievable from the tool itself. With loupe you can run loupe --schema from any installed binary and get back the full JSON Schema for its output.This matters more than it first appears. It means an agent encountering the tool for the first time can learn the entire contract without external documentation, without a web search, without a training-data memory of how the tool behaved in some prior version. The tool teaches itself.
The contract travels with the tool, versioned alongside it, so there is never a question of which docs apply to which binary.
It is high-context.
Agents are expensive to run and slow to round-trip; every call should earn its place. A good agent-first tool returns not the bare minimum but the relevant maximum — the data and the surrounding context an agent is likely to need next, gathered in one structured response. loupe’s --context flag, for instance, folds VCS state, project type, recently modified files, and any per-repo agent notes into the same typed payload. One call, much context, no follow-up archaeology.
It is bounded and deterministic. Recursion is depth-limited and explicit; ordering is stable across runs; an unreadable path produces an honest error rather than silently truncated output.Determinism is what makes agent behavior reproducible. If the same command can return differently-ordered output on two runs, every cache, every diff, and every test built on top of it inherits the nondeterminism. The reader can rely on the shape of what comes back, not just its contents.
None of these are radical. Taken together, they describe a tool that has decided, deliberately, that its reader is a machine — and has done the machine the courtesy of writing for it.
§ III · A worked exampleloupe, concretely
I built loupe as a small, honest test of this idea. It does something almost embarrassingly modest: it observes a filesystem path and emits typed JSON. It is, in one sense, just ls and find and stat wearing different clothes. But the clothes are the point.
Point it at a directory and it returns a typed tree — names, types, sizes as integers, modified times as Unix timestamps, permissions, entry counts, and nested entries down to whatever depth you ask for and no further. Pipe it to jq and you can answer a question like “which files here are larger than four kilobytes” with a filter instead of a parser. Connect it over MCP and the same typed output arrives as a first-class tool call inside an agent’s loop, with loupe_observe, loupe_output_schema, and loupe_notes_schema exposed directly.The MCP server is the same binary — loupe --mcp. There is no second process, no daemon, no separate contract to keep in sync. The CLI output and the MCP output are the same typed payload, because they are produced by the same code.
The piece I am most attached to is the notes layer. An agent can leave structured notes for the next agent in .loupe/notes.json at the git root — a gotcha, an invariant, a known issue — and loupe will surface them, typed and validated, in the context payload. It is a tiny thing, but it is the shape of the future I am arguing for: tools that are not just read by agents but written by them, accumulating high-context state that compounds across sessions.
Why I’m writing this to you
Here is the part that is harder to put in a schema. I think this matters, and I would like my colleagues to take it seriously — not because loupe is important, but because the pattern is.
We are about to spend the next several years handing more and more of our work to agents. If we hand them the toolchain we built for ourselves — text for eyes, formats that are really just habits, contracts that live only in our heads — we will spend those years debugging the seams between probabilistic parsers and typography that was never meant to be parsed. The failures will be subtle and expensive and hard to attribute, because a tool that is right ninety-eight percent of the time is exactly the kind of tool that quietly poisons the other two percent.
I am not asking anyone to rewrite the world. I am asking for something much smaller: the next time you build a tool an agent will use, decide up front that the agent is the reader. Type the output. Publish the schema. Return the context. Make it deterministic. It is a few hours of extra care, and it removes an entire class of failure that would otherwise be someone’s afternoon, months from now, with no idea why the numbers are wrong.
I am going to make this case with evidence, not just conviction. I am collecting numbers — task success rates, token cost per task, time-to-first-correct-action, retry counts — comparing agents working through human-formatted tools against the same agents working through typed, schema-defined ones. I will publish them here as they come in, and I will let them argue better than I can.
What I'm measuring — updated as data arrives
This is a placeholder for the figures that will make the case. As of now the table below is illustrative; I will replace each cell with measured values, with methodology, as the experiments run.
- Task success rate — fraction of multi-step filesystem tasks completed without a parsing-induced error. (collecting)
- Tokens per task — total tokens spent, including retries caused by malformed-output recovery. (collecting)
- Retries per task — how often the agent had to re-issue a command after misreading output. (collecting)
- Time to first correct action — wall-clock from prompt to the first action taken on correctly-read state. (collecting)
If you would like to contribute a workload to measure, or you have a tool you would like instrumented, the repository is the place to start.
What it looks like at scale
One typed tool is a convenience. A whole ecosystem of them is a different kind of thing entirely.
Imagine that every tool an agent reaches for — the file lister, the process inspector, the package manager, the test runner, the deploy script — speaks the same way: typed output, retrievable schemas, high-context responses, deterministic behavior. The agent stops being a translator shimming between incompatible human-facing formats and becomes what we actually want it to be: a planner, reasoning over reliable structured state.This is the quiet thesis under everything here. The bottleneck on agent capability is often not the model — it is the legibility of the environment. Make the environment legible and the same model gets dramatically more done. The tokens we currently spend on parsing and recovery get spent on reasoning instead. The errors we currently chase across the seams between tools simply stop occurring, because there are no seams — only contracts.
That ecosystem does not require a standards body or a grand rewrite. It requires a convention, adopted one tool at a time, by people who decided that the agent reading their output deserved a contract rather than a guess. loupe is my contribution to that convention. It is small on purpose, so that the idea is easy to see and easy to copy.
Above all else, show the data — to whoever, or whatever, is reading. For a century of statistical graphics that meant respecting the human eye. For the work ahead it means respecting a new reader, one that cannot squint at a column or infer from alignment, but that can do extraordinary things when we simply tell it, in types it can trust, exactly what is there.
This is a living document. I am collecting numbers as I go, and I will revise the argument — and the evidence below — as the data accumulates. If you are a colleague reading this, the appeal is sincere: try building one tool this way and tell me what you find.
Typeset in ET Book, with notes kept in the margin in the manner of Edward Tufte. Built with loupe in mind.
© Aaron Norling · Back to loupe · More essays