The Software GardenDesigning for agent users ← Contents
The Software Garden

Designing for agent users

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.

Scroll ↓
Two audiences, one tool

Humans want a table. Agents want data.

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.”

The contract

Every channel has one job.

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.

stdout
Data, and only dataJSON the agent can pipe straight into jq or its next step.
stderr
Messages, also as JSON{"status":"warning","message":"…"}: never mixed into the data.
exit code
0 or 1Success or failure, without parsing anything. --dry-run exits 0.
errors
Structured{"status":"error","message":"…"} on stderr, so the agent can decide what to do next.
Agent flags

An agent's attention is a budget. Don't make it pay for columns it doesn't need.

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.”

agent context window 
the tasktool output
 
 

Proportions are illustrative.

Safety

Let agents look before they leap.

--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.

Discoverability

The tool describes itself, in a form an agent can read.

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.

Never stale

Derived from the registry

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.)

Meet agents where they are

Docs and protocols for agents

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.

Beyond the CLI

The agent writing code against your SDK is a user too.

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.

Types that tell the truth“Type honesty”: a field marked optional really is optional at runtime. Semantic keys stop a JobKey being passed where a ProcessInstanceKey belongs.
Errors you can branch onGo: a typed *APIError plus sentinel errors, tested with errors.Is/errors.As. And no silent failure: no zero values or best-guess defaults.
The best example in the editorExamples injected into API docs and IntelliSense point to the ergonomic helper, not the raw call, so the first thing an agent sees is the best way.
One shape across languagesSeven SDKs share a structure, config conventions and AGENTS.md layout. An agent that learns one can work in all of them.
So

Agents are users. Design for them on purpose.

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.