I design scalable platforms and streamline infrastructure so your team can move faster, safer, and smarter.
© 2026 Raditya Surya. All rights reserved.
Why human CLIs waste tokens and MCP is not the answer. A look at AXI — the Agent eXperience Interface — and the Coolify CLI I built with it.
At home, almost nothing reaches my terminal by hand any more. I describe what I want, an agent runs the commands, and I read the result. Coolify runs most of my side projects, so a lot of those commands are Coolify commands. Restart this app. Show me why that container is unhealthy. Deploy the branch I just pushed.
The official coolify CLI is good. I like using it. The problem is that I am not the one using it any more.

Coolify ended up in the middle of my setup for the same reason most of my choices land where they do. It is the lightest thing that still does the job.
At work I deploy to EKS with Helm charts, and that weight is justified when downtime costs real money. At home it is a sledgehammer, which is the whole argument I made in My Semi-Industrial Homelab: Why I Keep Work and Play Separate. Coolify gives me the part of a platform I actually want. It connects to Git, it deploys on push, and it never asks me to write an ingress controller.
I called it a lightweight Heroku at home in My Lazy Self-Hosting Setup: Where Everything Just Works, and that is still the shortest description I have. Nineteen resources live on it now, and every one of them is something I would notice going down.
So when I started handing my terminal to an agent, Coolify was the first thing it needed to reach.
When I ask for a list of applications, coolify --format json returns about forty fields per application. Base64-encoded Traefik labels, full Dockerfile and compose paths, and health check settings I have never once looked at.
On my instance, with nineteen resources, that single command produces just over thirty thousand bytes. The agent needs four of those fields to decide what to do next. It pays for all forty.
Then there is addressing. Every resource is identified by uuid, so nothing can be asked for by name. To read the logs of one app, the agent lists everything, finds the matching row, extracts the uuid, and calls again. Three turns to answer one question. I would have done it in one, because I already know which app I mean.
That is the real gap. The CLI is not badly built. It was built for a reader who skims, recognizes what matters, and ignores the rest. An agent does none of those things. It reads every byte, and it pays for every byte.

The obvious answer is the Model Context Protocol, and it fails in the opposite direction.
An MCP server hands the agent a set of tool definitions up front. Those definitions sit in context whether the agent uses them or not, and before it can act it has to work out which tool applies, what the schema wants, and how the pieces fit together. Thiago Marinho put it better than I can in his post on AXI: the problem with a plain CLI is human legacy, and the problem with MCP is that discovery itself became work.
So one option is verbose, and the other is expensive before anything happens. Neither is wrong. Both are aimed at somebody else.

AXI stands for Agent eXperience Interface. It is not a protocol, and it does not replace MCP. It is a set of ten design principles for command line tools that agents will actually run, and its central claim is simple. The token budget is a design constraint, not an implementation detail.
The principles come from kunchenguid/axi, and they group into efficiency, robustness, and discoverability. In practice that means spending fewer tokens, behaving predictably when something goes wrong, and staying usable without a manual.
The most visible one is the output format. An AXI writes TOON on stdout instead of JSON. TOON keeps the JSON data model but strips the repetition out of lists, so field names appear once instead of once per row. On list-shaped data that is roughly forty percent fewer tokens for the same information, and the agent learns how many items exist without counting them.
Upstream measured this rather than argued about it. Across 425 runs on GitHub tasks, gh-axi completed every one at five cents per task, while the MCP conditions landed between 82 and 87 percent success at two to three times the cost.

So I built coolify-axi, a wrapper around the official CLI, and the principles changed almost every decision in it.
Minimal default schemas came first. A list returns four fields, not forty, and the detail view is where the rest lives. List, detail, and summary are three different products, and I had been shipping one of them three times.
Content first was the one I would not have arrived at on my own. Running coolify-axi with no arguments prints the live dashboard — every resource and its health — rather than a help screen. An agent that runs a command blind should learn the state of the world, not be handed a manual.
Then errors, which is where I had the most to fix. Exit codes mean something now: zero for success, one for failure, two for a usage mistake. Errors go to stdout in the same structured shape as everything else, so the agent can read them instead of guessing at stderr. Starting an app that is already running is a no-op that exits zero, so the agent can declare intent rather than checking first. And an unknown flag fails loudly, exits two, and names the valid flags for that subcommand inline, so the correction happens in the same turn.
That last one matters more than it sounds. A flag that is silently ignored is worse than an error. The agent gets plausible output it believes was filtered, and then acts confidently on the wrong data.
Truncation was the last piece. Logs are capped with a size hint and a --full pointer, so one noisy container cannot eat the entire context window.
Agent ergonomics turned out to be measurable, which I did not expect. Same instance, same nineteen resources:
| Command | Raw coolify --format json | coolify-axi | Reduction |
|---|---|---|---|
app list | 30,873 B | 764 B | 40× |
db list | 2,664 B | 491 B | 5.4× |
Forty times is not a rounding artifact. It is what happens when you stop shipping thirty six fields nobody reads.
The round trips went away too. Every command takes a name, so coolify-axi app logs digivaley is one call instead of three.
If a name matches more than one resource, it stops and lists the candidates rather than acting on the wrong one. Secrets are redacted by default, including passwords buried inside connection URLs, and --reveal is there for the times I actually want them.
AXI keeps a community catalog of tools built on the principles, so I forked the repository and opened a pull request to add mine. It was merged the same day.
coolify-axi is in there now, under the community section at axi.md, next to everything else people have built.
It is not the only AXI my agents run every day. The one they check before spending anything is quota-axi, which I wrote about in Multi-Provider AI Coding: My Agents Know Who Has Quota Left.
None of this was on my mind a year ago. Nobody ever billed me for the fields I skipped past, so I never counted them. My agent counts all of them, and then sends me the bill.
Building for it felt like extra work until I noticed what it replaced. Every field I removed is a field I no longer pay for.
Every round trip I collapsed is a turn I no longer wait through.
So I did not build coolify-axi because I care deeply about output formats. I built it because I am lazy, and this is what lazy looks like when the machine is the one doing the typing. 🐱