Agents don't only build our software. More and more, they're the ones using it.
So we design our tools for them, as first-class users.
c8ctl, the Camunda 8 CLI, serves both. People get aligned, readable tables. Agents switch to JSON with --json for one command or c8ctl output json for the session, and get output they can parse without guessing.
Scraping a table meant for humans is fragile: a column changes width or a header gets renamed, and the agent's parsing quietly breaks.
The repo even has a file just for agent consumers, CONTEXT.md: “invariants and conventions for AI agents consuming c8ctl programmatically.”
An agent composes tools the way a shell script does: pipe the data, check the exit code, read the error. That only works if each channel carries one kind of thing, every time.
In JSON mode, c8ctl writes data to stdout and operational messages (info, warnings, errors) to stderr as JSON. Failure is exit code 1 with a structured error. An agent never has to tell a warning from a result by reading English.
jq or its next step.{"status":"warning","message":"…"}: never mixed into the data.--dry-run exits 0.{"status":"error","message":"…"} on stderr, so the agent can decide what to do next.Every byte a tool prints lands in the agent's context window, crowding out the task. c8ctl has agent flags, listed in their own section of the help: --fields picks the keys to return, and --limit caps the results.
From CONTEXT.md: “Use this to reduce context window size when parsing output programmatically.”
Proportions are illustrative.
--dry-run shows the exact API request a command would send (method, URL, body) without executing it, as JSON, with exit code 0.
An agent can check that it built the right request before it cancels a process instance. A human, or a reviewing agent, can approve the intent instead of the outcome.
Inside c8ctl, each command declares whether it is mutating. That kind of metadata lets the tool, and its users, treat destructive actions differently.
Terminal output on this page is abbreviated and illustrative; the flags and output shapes come from c8ctl CONTEXT.md.
In JSON mode, c8ctl help returns the whole command tree as structured data: commands, globalFlags, searchFlags, agentFlags, resourceAliases. An agent can learn the tool by asking it.
Help, completions, argument parsing and validation all come from one COMMAND_REGISTRY. What the agent is told is exactly what the tool accepts. (See Derivation over duplication.)
CONTEXT.md states the invariants an agent can rely on. c8ctl mcp-proxy bridges to an MCP server, for agents that speak the Model Context Protocol.
An agent learns an API from its types, its errors and its examples. Most of the time it doesn't read the docs. So the SDKs put the truth where the agent looks.
The same choices help humans. Designing for agents mostly means taking away the ambiguity a human would have quietly worked around.
JobKey being passed where a ProcessInstanceKey belongs.*APIError plus sentinel errors, tested with errors.Is/errors.As. And no silent failure: no zero values or best-guess defaults.Give them data, not prose. Give each channel one job. Respect their context budget. Let them preview before acting. Make the tool describe itself from its single source of truth. And make types, errors and examples tell the truth, because that's where agents look.