Your API's first reader is an agent
The first consumer of a new service used to be a developer with a README. Now it's usually a coding agent with a context window. The service should hand that reader its own manual.
Think about what actually happens when someone points Claude or Cursor
at your API today. The agent fetches
/openapi.json, gets a wall of JSON designed for machines
that render documentation, and starts inferring. OpenAPI is the right
interchange format, but it's a poor first read: heavy on structure,
silent on intent, expensive in tokens.
The llms.txt convention emerged for exactly this reader: a plain markdown index at a well-known path, written to be loaded into a context window. Documentation hosts generate it for doc sites. For live APIs, almost nobody serves one, because it's yet another artifact to keep in sync with the routes. That objection is the interesting part. A framework whose whole thesis is "one schema, every boundary" doesn't have a sync problem.
Served by default, derived from the registry
As of this week, app.start() serves two new paths
alongside /openapi.json. Nothing to configure, nothing to
regenerate:
GET /llms.txt # compact index: one line per operation
GET /llms-full.txt # every operation with its schemas inlined
Both render from the same assembled OpenAPI document the spec endpoint
uses, which renders from the same Zod schemas the dispatcher validates
with. Add a route, and the index updates. Tighten a schema, and the
inlined JSON Schema in
/llms-full.txt tightens with it. The documents also
describe the error envelope, so an agent learns what failures look
like before it sees one.
# orders-service
> Order management for the storefront.
This service is described by an OpenAPI 3.1 document at `/openapi.json`.
## Endpoints
- `GET /orders/{id}` — Fetch one order
- `POST /orders/{id}/refund` — Refund an order and notify the customer
## MCP (Model Context Protocol)
This service is also an MCP server: connect over Streamable HTTP at `/mcp`.
Tools:
- `refund_order` — Refund an order and notify the customer.
That MCP section isn't hardcoded. Components contribute sections by
binding values under the AX_SECTION_TAG binding tag, and
installMcpHttp contributes the tool listing when you
mount the MCP transport. The same DI discovery that wires servers to
controllers wires documentation to the components that have something
to say. Your own components can join in with one binding.
A manual you can commit
Endpoints work for agents that browse. For coding agents working in a repo, the better delivery is a file they load automatically:
const spec = await server.getApiSpec();
await fs.writeFile(
'AGENT.md',
generateAgentContext(spec, {baseUrl: server.url}),
);
The output is a CLAUDE.md-shaped manual for the live API: base URL, auth schemes, the error contract, and every operation with its schemas. Regenerate it in CI and the manual can't drift from the service, because both are projections of the same registry.
Why this is a framework feature
Any team could write this generator in an afternoon, and that's the problem: it becomes one more artifact someone owns, three sprints from being stale. The reason to push it into the framework is the same reason OpenAPI emission lives there. Hand-maintained contracts drift; derived contracts can't. If the first reader of every new API is an agent, then "describes itself to agents" belongs next to "validates its inputs," not in a wiki page about best practices.