We didn't build for agents. We built a good CLI.

enum is agent native without being agent native.
We have no MCP server and no agent SDK yet. Both are coming. What we have today is enumctl, a CLI we built because we wanted a cloud that is pleasant to use from a terminal. Then coding agents showed up in everyone’s terminal, and it turned out they wanted the same things we did.
This post is about those things. None of them were designed for agents. All of them are why an agent can already run enum without special treatment.
The same user, twice
Think about what a developer wants from a CLI late at night, halfway through something else:
- Commands that do what their name says.
- Output that can be piped into the next tool.
- Errors that say what to do next.
- No detour through a browser for something that could have been a flag.
Now think about what an agent needs. It cannot click through a web console. It cannot guess what a spinner means. It reads what the command prints, decides what to run next, and gets stuck on the first prompt nobody told it about.
That is the same list. An agent is a developer with no patience and no browser, and a CLI that respects the first kind of user works for the second.
What that looks like in enumctl
Every command speaks three formats
--output is a global flag: table for people, json and yaml for everything else. It works the same on every command that prints a resource.
enumctl dns zones list -o json
An unknown format is rejected before the command does any work, so a typo never costs you an API call or leaves you with half an answer.
The data goes to stdout. Everything else (success messages, warnings, hints about a new version) goes to stderr. Piping enumctl into jq gives you the data and nothing but the data.
It knows when nobody is watching
A spinner is nice when a person is waiting. In a log file it is noise. enumctl only draws one when it is attached to a terminal, and the same goes for the update hint.
Prompts follow the same rule. When stdin is not a terminal, enumctl does not sit there waiting for an answer that will never come. It stops and says which flag it needs:
$ enumctl init < /dev/null
✗ Non-interactive mode requires --project-id.
The non-interactive version is one line:
enumctl init --project-id proj-... --yes --device
--device is there because of SSH sessions. A login that opens a browser is useless on a machine without one, so there is a device flow: enumctl prints a URL, and you open it on whatever device has a browser. A person on a remote box needs that. So does a sandboxed agent.
Commands read the way a human would say them
Ask a person to add a DNS record and they will say the name, the type and the value. That is the command:
enumctl dns records create www.example.com A 203.0.113.42
That is all there is to it. The command holds exactly what you would say out loud, in the order you would say it.
A human would also say “add a record” as readily as “create a record”, so add works wherever create does on DNS commands. An agent makes the same guess and gets the same result.
You can look before you leap
Importing a zone file is the one DNS operation that changes many records at once, so it has a preview:
enumctl dns zones import example.com --dry-run < example.com.zone
It reads from stdin, prints the changes it would make, and makes none of them. For a person this is reassurance. For an agent it is the difference between a plan a human can review and an action nobody saw coming.
It can explain what is wrong with itself
enumctl doctor checks the config file, the stored credentials, the connection to the API and the identity provider, and whether the configured organization and project still exist. It changes nothing and starts no login. Every failed check names the command that fixes it, and the exit code is non-zero if anything failed.
A person who is stuck runs it before opening a ticket. An agent that hits a wall has a first move that is better than retrying.
The terminal is a first-class client
Signing up starts in the terminal: enumctl init offers to create an account, asks the registration questions, shows the terms, and waits while you confirm your email address. Logging in goes through the browser once. After that, creating a project is a prompt or a single command.
This works because the console and the CLI are not two products. The registration form, for example, is served by the API, and both clients render what they are given. There is no feature that exists in the browser first and reaches the CLI later.
A person still has to sign up. There is a captcha, a confirmation email and a login in the browser, and that is on purpose. Humans create accounts. Agents operate them.
The docs are for both readers
docs.enum.co publishes an llms.txt index and an llms-full.txt with every page in a single file. Same content, same source, one more format. An agent that needs to know how DNSSEC works on enum reads the page you would read.
Try it
Install enumctl, run enumctl init, and hand the terminal to whoever is doing the work today. The quickstart gets you from an empty account to a live DNS zone.