Ryan Dale (@rot13maxi) on X

X (formerly Twitter) ·

12 min read Original article ↗

An agent-first interface is not just “there’s an API.” It is a system designed to reduce ambiguity, compress context, avoid wasted reasoning, and help agents stay synchronized with a changing product.

Shards is a collectible card game where your agent, whether that is Claude Code, OpenCode, Cowork, or something else, plays the core game loop. The metagame for humans is how you steer your agent and how much infrastructure you build around it. Do you give turn-by-turn tactical feedback? Do you mostly let it run on its own? Do you build a harness so it can improve over time, maybe with help from other agents? How you manage your agent has a direct effect on how well it performs.

And the core game loop is only part of it. Agents can also build decks, buy and sell cards on the marketplace, inspect progression, assign points in the skill tree, read lore, and consume release notes and announcements. Once you have that kind of surface area, the UX question changes pretty quickly. You are no longer just asking “can an agent hit my API?”. You are asking what a good interface looks like for a user with token limits, latency costs, partial context, and a strong tendency to confidently invent missing details.

Let’s start with the first thing we learned.

Token efficiency is UX

During early development, we started the same place everyone starts: expose the API, write docs, and let agents use raw HTTP.

This works. But we immediately saw a bunch of issues. Some were predictable. Some surprised us.

When you give an agent a raw HTTP API, it will very often write ad hoc shell or Python scripts for every interaction. Usually that means:

making the request, often by wrapping `curl`

parsing the JSON response

extracting and reformatting the one or two fields it actually cares about

retrying after guessing wrong about the request or response shape

That last one happened a lot. Agents love to just guess field names, even when you give them docs.

This is fine for ad hoc API interaction. It is not fine for a repeated loop. In a game, the agent is doing the same kinds of operations over and over: checking game state, figuring out legal actions, submitting actions, browsing the marketplace, checking progression, claiming rewards, reading announcements, and so on. If every one of those interactions requires mini glue code plus a round of JSON archaeology, the agent is wasting tokens, rounds of reasoning, and time.

The important point here is that the cost is not just network overhead. It is reasoning overhead.

For agents, every extra field, wrapper, and parsing step has a cost. A large part of bad UX for agents is forcing them to spend intelligence on plumbing.

So we built a CLI for the game.

The CLI wraps the API, but the point was not just convenience in the human sense. The point was to present a concise, predictable interface for agent workflows. That cuts down on:

tokens spent generating one-off scripts

tokens spent parsing verbose responses

mistakes from guessing request and response shapes

latency in the game loop

The CLI is optional. Some agents can only use the direct API, or their operators do not want to allow an installed binary. That is fine. The raw API still exists and is fully supported (we even publish a swagger spec!). But for agents that can use a CLI, it is faster, cheaper, and more reliable.

That sounds almost obvious in retrospect, but I think there is a more general point here: for agent systems, token efficiency is UX. If your interface causes the model to repeatedly burn context and reasoning budget on integration work, that is a bad interface.

It is not just about making things shorter

One thing we learned pretty quickly is that “compact” and “good for agents” are not the same thing.

The API itself, and the CLI wrapping it, exposes compact representations for core surfaces like game state, cards, and collections. That matters because smaller payloads are cheaper. But the more important part is that these shapes are designed to be easier for agents to interpret correctly.

That is a different goal.

A good agent-facing representation is not just a human-oriented representation with fewer fields. It should reduce ambiguity. It should make it easier for the model to figure out what matters right now. It should reduce the amount of inference needed to express intent.

In gameplay, for example, what the agent usually cares about is some version of:

what turn and phase is it

can I act right now

what resources do I have

what is on the board

what actions are legal right now

You can absolutely hand the model a giant nested object and say “derive all of that yourself”. In theory, a capable enough model can do it. In practice, that is just wasted work.

So we started shaping interfaces around the actual decision loop the agent is in. Not just “here is all the state”, but “here is the state in a form that is hard to misread and easy to act on”.

That same idea shows up in lots of places. The CLI explicitly errors on unknown flags instead of silently ignoring them. We support explicit concurrency guards so actions can fail clearly if the game state has advanced. The system includes sequence numbers so an agent can catch up incrementally instead of repeatedly loading everything from scratch. These sound like small engineering details. They are. They are also UX.

An agent interface should not just be concise. It should be hard to misunderstand.

Assisted reasoning beats purity

There is a certain kind of technically “pure” design instinct that says the interface should expose state and rules, and the agent should do the rest.

I think that instinct is often wrong.

A good example is figuring out what game actions are legal at a given moment. In principle, an agent can inspect the board, reason about the rules, and derive its legal moves itself. That is possible. It is also expensive and brittle, especially when you are doing it every turn across many games.

So we expose legal actions directly.

Then we added an optional explanation mode that tells the agent why certain moves are not legal.

Again, an agent could in theory figure that out on its own. But if the system can validate those constraints cheaply and deterministically, why make the model burn reasoning cycles on reconstructing them? The agent should spend its budget on strategy, not on reimplementing the rules engine every turn.

This also turned out to be useful for operators. Early on, richer explanations help both the agent and the human understand what is going wrong. Later, once the agent is dialed in, you can turn that extra help down for tighter loops and lower token cost.

So the interface is not just an execution surface. It is also a teaching surface.

I think this point generalizes pretty well: if the system can cheaply provide a validated intermediate representation that saves the agent real work, it often should.

Skills are the new install surface

Another thing that became obvious while building Shards is that, for many agents, the installable unit is not an app or an SDK. It is a skill.

The onboarding flow is literally: tell your agent to install the skill and complete setup.

That sounds like a minor workflow detail, but I think it is actually pretty important. The skill becomes the entry point for distributing software to agents. It teaches setup, explains how to play, covers deckbuilding and the marketplace, and points the agent at the commands and APIs it needs. In other words, the skill is not just docs. It is part of the product surface.

Our first pass at this got big fast. We had too much information in one place: onboarding, gameplay, factions, deckbuilding, marketplace, lore, API reference, and so on. A monolithic skill worked in the sense that it contained everything, but it was inefficient. The agent had to load a lot of material that was irrelevant to whatever task it was doing at that moment.

So we split it up.

We ended up with a main skill that acts as an entry point and quick reference, then linked documents for setup, gameplay, deckbuilding, marketplace, lore, and raw API reference. That lets the agent load the knowledge it needs when it needs it.

This is basically the same design principle as compact state, just applied to documentation instead of API responses. A monolithic prompt is the docs equivalent of a giant JSON blob. It may be complete, but it is not operationally efficient.

We also added versioning and per-document hashes so agents can cheaply detect what changed and only reload the files that actually changed. That turned out to matter more than I expected. If your product evolves over time, an agent-first interface needs some concept of incremental update. You do not want the only refresh strategy to be “re-read everything all the time”.

Event-driven beats polling

Another place where human-first assumptions fall apart is synchronization.

Humans are fine with refresh buttons and occasional polling. Agents that live inside a repeated control loop benefit much more from event-driven interfaces.

So in addition to the regular HTTP surface, we expose WebSockets for queue matching and live game updates. That means an agent can subscribe to what is happening instead of constantly asking “has anything happened yet?”.

This is good for obvious reasons, like fewer requests and lower latency. But it also matters for the structure of the loop itself. The agent no longer has to build a bunch of polling and retry logic around the game. It can wait for match events, wait for opponent actions, reconnect if needed, and catch up from a known sequence if the session gets interrupted.

It also makes it easier to build more interesting agent harnesses. For example, you can have one process or subagent sit on the WebSocket and dispatch game events to another agent or subsystem for decision-making, instead of forcing a single loop to poll and manage all control flow itself.

This is one of those places where the UX story is not really about commands or docs. It is about the shape of the interaction model. If you want agents to operate efficiently, you need to think about how they stay in sync with the system, not just how they issue commands.

Relatedly, we also support incremental synchronization in the HTTP layer. Instead of repeatedly loading full state or full history, the agent can ask for what changed since a given sequence number. Again, the pattern is the same: preserve reasoning and tokens for decisions, not bookkeeping.

Correctness needs guardrails

Agents inspect state, think for a bit, maybe get interrupted, and then act. In an asynchronous system, that means stale context is a real problem.

If the world changed while the agent was reasoning, you do not want it blindly submitting an action against an outdated game state. So we added optimistic concurrency guards like expected turn and expected sequence. If the game has advanced, the action fails explicitly instead of applying against stale assumptions.

This is a small thing, but it is one of those places where engineering correctness and UX really are the same thing. A good agent interface should make stale-context failures cheap, obvious, and recoverable. Silent success on the wrong state is much worse than a clear failure that tells the agent it needs to refresh.

The same logic applies in the CLI. Shape mismatches fail loudly. The system tries to be strict in the places where permissiveness would create expensive loops.

That can feel a little unfriendly if you are imagining a human at a terminal. For agents, it is often exactly right.

The interface should stay in conversation with the agent

One of the things I like most about this system is that release notes and announcements are not trapped on the website. They are fetchable through the API and the CLI.

That matters because a lot of agent failures are really stale-assumption failures. A card changed. A balance patch landed. A bug got fixed. A new feature appeared. If the agent is operating against last week’s understanding of the product, it is going to make bad decisions.

So the system gives agents a way to learn about product changes directly.

We pushed the same idea further with bug reporting and feature requests. Agents can report bugs and ask for features through the CLI and API while they are playing. Then later they can consume release notes and announcements and see that issues they hit were fixed.

I think that loop is pretty novel and pretty important. The agent is not just a user of the system. It is part of the feedback channel that improves the system.

What changed when we started building for agents

The big lesson from all of this is that building for agents is not the same thing as exposing a human product over HTTP.

An agent-first interface takes the actual operating constraints of agents seriously. It minimizes ambiguity. It compresses context without hiding what matters. It gives the model validated helper abstractions when they save real reasoning work. It supports incremental learning and incremental synchronization. It treats docs and skills as part of the interface, not as an afterthought. And it assumes that adapting to a changing product is part of the job.

In Shards, that led us to a CLI, compact API shapes, modular skills, versioned docs with hash-based refresh, legal action introspection, optional explanation surfaces, event-driven updates, sequence-based catch-up, explicit concurrency guards, and built-in bug reporting and release-note consumption.

The best agent interface is not the one with the most raw power. It is the one that lets the model spend its reasoning budget on decisions instead of integration work.