
An agent cannot use a capability it cannot find, cannot call, and cannot read the result of. That sentence is the whole of agent-native architecture, and almost every product that fails the test fails it for the same reason: the capability was designed for a person looking at a screen, and the machine-facing surface was added afterwards as a thin mirror of the database.
This is a design guide for the other direction. It assumes you are building a developer tool, an internal platform, or a product feature, and that within a year a meaningful share of your users will be agents acting for people. The definition of agent-native and the argument about what the term means are covered elsewhere. This page is about what you actually change in the code.
The four properties, restated as design constraints
An agent-native system has callable actions, artefacts that live as files, state it can read back, and a human gate that sits at review rather than in the middle of the loop. As a checklist that is unremarkable. As constraints they are demanding, because each one rules out a design that is otherwise the obvious one.
- Callable actions rule out capability that exists only behind a click handler.
- File artefacts rule out the vendor console as the place work lives.
- Readable state rules out returning a link to a dashboard as the result of an operation.
- A late human gate rules out step-by-step approval as the safety story.
Most products violate at least three. The good news is that the fixes compound, and the first one is cheap.
What breaks when you bolt an API onto a human-first product
Human-first products put the workflow in the interface. A wizard sequences four calls, holds intermediate state in component memory, formats the result client side, and posts to internal routes that were never meant to be a contract. The public API, if there is one, exposes the nouns: users, projects, records. It exposes create, read, update, delete on each.
An agent handed that API hits four predictable walls.
The verbs are missing. There is an endpoint to create a record and an endpoint to update it, but the job the user actually wants ("run the import and tell me what failed") exists only as a sequence the front end knows. The agent has to reimplement the wizard, and it does not know the ordering rules because they were never written down anywhere but in JSX.
The output has no meaning. The API returns rows. The chart, the diff, the pass/fail badge, the "3 of 40 items were skipped" line, all of that is computed in the browser. The agent gets data and no judgment, so it either guesses or hands back to the human.
Auth is session-shaped. Cookies, CSRF tokens, a login form with a captcha. Programmatic tokens exist but are scoped to the whole account because scoping was only ever a UI concept.
Errors are written for a form field. 400: invalid input tells a person to look at the red outline next to the field. It tells an agent nothing about which field, why, or what a valid value looks like.
The fix is not more endpoints. It is to expose jobs rather than tables. Write down the ten things users actually come to your product to do, phrased the way they would say them out loud, and make each one a single callable operation that takes the inputs a person would give and returns the answer a person would want. If a job needs four internal calls, the sequencing belongs on your side of the line, not the caller's.
A dashboard-only capability is invisible
This is worth stating on its own because it is the most expensive mistake and the easiest to keep making. If the only path to a capability is a button, then for an agent that capability does not exist. Not "is harder to reach". Does not exist.
The organisational fix is a rule at the pull-request level: a feature is not done until it has a non-UI entry point shipped in the same change. Retrofits never happen, because retrofitting means reconstructing intent from a controller written eighteen months ago.
Discovery is the second half of the same problem. Agents find capability through a manifest: the tool list an MCP server advertises, a CLI's help output, a skill file describing a procedure. Anything not in the manifest is invisible even when the endpoint exists, because nothing told the agent to look.
That makes the manifest a design surface, and it has a budget. Every tool description sits in the model's context on every turn, and a server with sixty near-identical tools both burns that budget and raises the misfire rate, because the agent now has to pick between update_record and patch_record_v2 on the strength of two sentences. Prefer a small number of well-parameterised tools over one tool per screen. Write the description as the only documentation the caller will ever read, because it is: name the preconditions, say what the tool returns, and say when not to use it.
Designing outputs an agent can parse
Getting the call right and the response wrong wastes the whole integration. Some rules that hold up in practice.
Return the result, not a pointer to it. {"status": "started", "url": "https://app.example.com/runs/8123"} is a dead end. If the operation is long-running, return a handle plus a way to fetch the outcome as data, and make the outcome self-contained enough to act on.
Include the judgment, not only the evidence. If your UI would show a red badge, the API should say "passed": false with a reason, not leave the agent to infer it from an array of timings. Meaning that only exists in your front end is meaning you have withheld.
Budget the bytes. Context is the scarcest resource an agent has. Paginate by default, truncate with an explicit marker rather than silently, and offer a summary mode alongside the full body. Returning a four megabyte DOM dump is a denial of service against your own user.
Make errors actionable and machine-readable. A stable code, a human-readable message, and where possible a hint naming the next call. {"code": "auth_required", "hint": "run the auth setup step, then retry"} closes a loop that 401 Unauthorized leaves open.
Be stable across runs. Deterministic key ordering, no volatile fields in the body when they are not part of the answer. Agents diff outputs. Noise in a response reads as a change in the world.
Hand back file paths, not ephemeral URLs. A screenshot the agent can open from disk is usable. A signed URL that expires in five minutes and requires a browser is not.
Artefacts belong in files
The thing your product produces has to live somewhere the agent can read, edit, and diff without asking you for permission. A row in your database fails all three, whatever the API around it.
Files win for reasons that are mostly not about agents at all. They diff, so a change is reviewable in a pull request with no new tooling. They are version controlled, so a bad change reverts. They survive the vendor, which is the thing procurement asks about and engineers feel later. And an agent can open one, change three lines, and save, which is a much shorter loop than read-API, transform, write-API, hope the write semantics matched.
Practical guidance on format: line-oriented and human-readable, so diffs stay legible, with comments allowed, because intent that cannot be recorded next to the thing gets lost. Avoid a format where a one-word change rewrites the whole file, since that destroys the diff and with it the review. And keep the file authoritative: your database can index or cache it, but the moment the database is the source of truth and the file is an export, you are back to rows.
Permissioning and blast radius
An agent will do exactly what you allowed, at machine speed, possibly in a loop, occasionally on the wrong target. Design for that rather than for a careful human.
- Split read from write in the tool surface. The read half should need credentials of little consequence, so the common case (an agent looking around before acting) never requires handing over anything dangerous.
- Scope credentials to a project and an environment, not to an account. Account-wide tokens are the norm because the console only ever had account-wide users. Agents make that a real exposure.
- Give destructive operations their own verb.
delete_environmentas a separate tool is a decision.update_environmentwith{"destroy": true}is an accident waiting for a bad parameter fill. - Default the target to non-production. Requiring an explicit, differently-named argument to touch production is cheap and prevents the worst class of mistake.
- Ship a dry-run mode that returns the real response shape. It lets an agent check its own plan, and it lets you test the integration without side effects.
- Accept idempotency keys. Agents retry on timeout. Without a key, a retry is a duplicate.
- Cap spend and rate, and fail loudly with a code. A silent throttle looks to an agent like an empty result, and it will act on the emptiness.
- Log the actor separately. When an agent acts for a person, the audit trail needs both. "Who did this" now has two answers and your log should carry them.
Where the human gate belongs
Supervision is part of every serious definition of agent-native, so the interesting question is placement, not presence.
The default should be late and on the artefact. The agent works to completion, produces something reviewable (a diff, a pull request, a report with evidence attached), and a person reads it and decides. Reading is fast. Approving twelve intermediate steps is not, and a person who is asked to click "continue" twelve times stops reading by the fourth.
Two cases genuinely belong earlier. The first is irreversible external effect: money moving, mail going to customers, data being destroyed. Put an explicit confirmation on those specific operations and nowhere else, so the confirmation still carries signal. The second is ambiguous intent at the start, where a cheap plan-then-confirm step saves an expensive wrong run. Both are narrow. If your product needs a human in the middle of the ordinary loop, that is usually a sign the outputs are not readable enough for the agent to decide on its own.
One more rule that matters more than it sounds: whatever the agent produces, something other than the agent should check it. A system where the same process does the work and grades the work has no gate at all, only a report.
Turning an existing product agent-native
A workable order, cheapest first:
- List the ten jobs in the user's words. Not the entities. The jobs.
- Ship a CLI covering them. It is the cheapest callable surface, it is testable in CI, it composes with everything, and it forces the output-design questions above into the open.
- Fix the outputs until a person could act on stdout alone with no dashboard open.
- Move the artefact into the user's repository as a file, and make your storage the index rather than the record.
- Wrap the CLI in an MCP server so agents get discovery and typed parameters rather than shell-string guessing.
- Write the procedures as skills, because knowing which tools exist is not the same as knowing the order to use them in. That split is worth understanding properly: see MCP servers vs agent skills.
A worked example
Shiplight is built this way, and it is a useful illustration because end-to-end testing exercises every one of the constraints at once.
The callable surface is a browser MCP server exposing a small set of deterministic tools: open a session, inspect the page, act on an element, read the console and network logs, close and collect the trace. Browser actions are deterministic, so the server itself needs no model key; the coding agent supplies the reasoning. The artefact is a YAML test file written into the user's own repository, so it diffs, it lands in a pull request, and the agent can edit it directly rather than through a console. State is readable in the sense that matters: a failing run comes back as a diagnosis with evidence rather than a red mark to interpret. And the human gate is late and on the artefact. Tests are plain-language YAML you read like a spec, and in CI a repaired test arrives as a pull request that never auto-merges.
The pattern generalises past testing. Small deterministic tool surface, procedures on top, artefacts as files in the user's repository, results that carry judgment, human review at the end.
FAQ
Is agent-native architecture just good API design?
There is heavy overlap, and a well-designed API is most of the way there. The parts that are genuinely different are discovery through a manifest the model reads at runtime, output sized for a context budget, artefacts stored as files the caller can edit, and permission scoping built for something that acts in a loop.
Do I need an MCP server, or is a CLI enough?
A CLI is enough to be callable, and it is the right first step because it is cheap and testable. An MCP server adds runtime discovery and typed parameters, which removes the guessing an agent does when constructing shell strings. Most teams should ship the CLI first and wrap it later.
How many tools should an MCP server expose?
Fewer than feels natural. Every tool description occupies context on every turn, and near-duplicate tools raise the chance the agent picks the wrong one. Prefer a handful of well-parameterised operations over one tool per screen, and write each description as the complete documentation, because it is.
Where should the human approve things?
At the end, on a reviewable artefact such as a diff, a pull request, or a report with evidence attached. Reserve mid-loop confirmation for operations with irreversible external effects. Step-by-step approval trains people to stop reading, which is worse than no gate.
What is the single highest-value change for an existing product?
Give every capability a non-UI entry point, enforced as a rule on new features rather than as a migration project. Retrofits stall because nobody can reconstruct the intent behind an old controller, while a per-feature rule costs little and compounds from the day you adopt it.


