---
title: "Agent Experience: Developer Experience for the AI-Integration Era"
description: "Developer experience is being renamed. The reader at integration time is increasingly an agent, not a human. Here's the playbook we used to redesign MyAgentMail's docs around that — llms.txt, paired HTML + markdown API reference, SKILL.md, MCP server, a CLI with Stripe-style browser pairing, and error messages written for the LLM that's about to read them."
date: "2026-04-29"
author: "myagentmail"
tags: ["developer-experience", "agent-experience", "ai-agents", "documentation", "mcp", "cli", "llms-txt"]
image: "/blog-images/agent-experience-developer-experience-ai-era.png"
---

# Agent Experience: Developer Experience for the AI-Integration Era

For most of the last decade, "developer experience" was a fairly settled discipline. You wrote good API docs, shipped a typed SDK in two or three popular languages, built an interactive portal with try-it-now buttons, and obsessed over the time-to-first-call. The integration funnel started when a developer opened your homepage; success was measured in how quickly they got a 200 back.

That funnel is changing in real time. Increasingly, the entity that opens your homepage is not a developer — it's an agent the developer told to *"go integrate Stripe"* or *"build me a thing using MyAgentMail."* The agent reads your docs, writes the code, and only escalates to the human when something is genuinely ambiguous. The human's role has compressed: they specify intent, review the diff, ship.

This shifts what good DX looks like. The skills that compounded over a decade — clear conceptual prose, hand-curated tutorials, rich interactive playgrounds — still help, but they're tuned for human cognitive bandwidth. Agents have different bottlenecks. They can ingest 200 pages of docs in seconds; they can't easily disambiguate between three pages that all describe authentication. They don't get bored or frustrated; they do hallucinate when the spec is missing a field. They don't read installation tutorials linearly; they fan out across every page on your site at once and choose what looks authoritative.

We built MyAgentMail as a developer product from day one — email infrastructure on demand, LinkedIn intent monitoring on demand, all API-driven, with a CLI (Stripe-style browser pairing) and an MCP server alongside the REST API. Our customers were never going to integrate by clicking through a dashboard. The signals around us — Cursor at $100M ARR, Copilot's agent mode, MCP adoption inside Claude Desktop and most modern IDEs, JetBrains' 2025 developer survey showing AI assistants used in over 80% of integration work — all pointed to the same thing: the reader of our docs at integration time was no longer reliably a human. So we rebuilt the documentation strategy around that assumption.

The result: an agent given just our URL, with no other context, can self-orient, find the right specs, install the CLI, the SDK, or the MCP server, and get a working integration in minutes — without hallucination, without human babysitting, without trial-and-error against our endpoints.

This post is the playbook we used. Seven things we changed, ranked by how much each one structurally affects an agent's first-pass success.

## 1. Surface a discovery file at /llms.txt

The single highest-leverage thing we shipped. There's an emerging convention — [llmstxt.org](https://llmstxt.org) — that says: put a markdown file at the root of your domain called `/llms.txt` containing the curated entry points an agent should read. It's the `robots.txt` of the agent era, except where `robots.txt` tells crawlers what to skip, `llms.txt` tells agents what to prioritize.

The format is intentionally minimal:

```markdown
# Project Name

> One-line summary in a blockquote.

Optional body paragraph with high-level context.

## Authoritative specs

- [SKILL.md](url): one-line note explaining what's in it.
- [OpenAPI](url): every endpoint, every shape.
- [Reference for X](url): when you need the deep dive.

## Code packages

- [npm package](url): typed SDK.
- [MCP server](url): drop into Claude / Cursor / Windsurf.

## Optional

These are skippable when context-budget-constrained.

- [Background reading](url)
```

Two things make this format work for agents specifically:

1. **It's a curated link list, not a sitemap.** A sitemap dumps every URL on the domain. An agent doesn't want every URL — it wants the four or five that contain the authoritative specs and the working install commands. `llms.txt` is editorialized in a way that mirrors how a good engineer would Slack you: *"Read these in this order."*
2. **The `## Optional` section is the load-bearing innovation.** It tells agents what to skip when context-budget-constrained. Agents have hard token limits; signaling the lower-priority pages directly is the equivalent of a senior engineer saying *"you can skip the marketing pages."* Without it, agents waste context fetching content that isn't load-bearing for the integration.

We added a companion at `/llms-full.txt` — same idea, but instead of *linking* to the docs, it *inlines* them. ~1,700 lines of concatenated reference material in a single fetch. Some agents prefer one round-trip over multiple even at the cost of slightly more tokens. Both Anthropic's MCP docs and FastHTML serve this dual file shape, and after watching agents consume our docs for a week, we copied it.

The lift to ship `/llms.txt` is roughly an hour of careful curation. It's the cheapest structural win on this list.

## 2. Pair every API endpoint with a markdown alternate

The week after we shipped `/llms.txt` we noticed something: agents fetched it, found the OpenAPI spec link, and then either parsed 2,500 lines of YAML to answer one question, or fetched our hosted reference (Scalar) and tried to scrape rendered HTML. Both are bad shapes. YAML asks the agent to do the rendering work; HTML asks it to undo our rendering work. The right shape sits between: per-endpoint pages, each available in two formats — HTML for humans, markdown for agents — at *paired URLs*.

So we ripped out the third-party docs renderer and rebuilt `/docs` from the OpenAPI spec ourselves. Two routes per endpoint:

- **HTML for humans.** `/docs/<group>/<operation>` — schema tables, response variants, a sticky code-samples panel with TypeScript / Python / curl. Live example: [`/docs/domains/listdomains`](/docs/domains/listdomains). Same design system as the rest of the site, no third-party iframe.
- **Markdown for agents.** `/docs-md/<group>/<operation>` — same content, no chrome, optimized for fetch-and-ingest. Live example: [`/docs-md/domains/listdomains`](/docs-md/domains/listdomains). An agent that needs one endpoint's schema doesn't have to open a browser, doesn't have to parse YAML, and doesn't have to undo HTML — it does `curl /docs-md/<slug>` and gets exactly what it needs.

Each HTML page also embeds an "AI actions" toolbar at the top — copy as markdown, view as markdown, open in Claude, open in ChatGPT — pattern stolen wholesale from Stripe's docs. The human reading the page can hand it to their agent in one click without a copy-paste round-trip that loses formatting.

The structural insight: anything you publish primarily for humans should have a markdown sibling at a predictable URL. Blog posts already do this on most modern sites (the `.md` alternate convention). API references should too. Every endpoint, every example, every reference doc — paired URLs, one HTML, one markdown, linkable from the human page so the human can hand the URL straight to their agent.

This is also the right shape because of the alternate-link convention in HTML. Every page on our site has `<link rel="alternate" type="text/markdown" href="...">` in the head. HTML-aware agents discover the markdown alternate from the head tag without us having to teach them where to look.

## 3. Write a SKILL.md that fronts the entire integration

`SKILL.md` is Anthropic's convention for agent-discoverable capability documents — frontmatter (`name`, `description`) plus a body of instructions. It looks at first glance like a glorified README. The difference is what goes at the top.

Our `SKILL.md` opens with three things, in order:

1. **A frontmatter `description` that tells the agent when to use this skill.** Not a marketing pitch — a precise operational statement. *"Use when building agents that need two-way email, intent-based outreach, multi-step campaigns, inline 2FA wait, or multi-tenant SaaS via isolated workspaces."*
2. **The auth model** — four key prefixes, when to use each, with the rule of thumb (*"always use the narrowest key that fits"*). An agent reading top-down knows about authentication before it reads about any endpoint, which matches how it actually needs to write code.
3. **A "Quick start for agents" section** with four integration paths ranked by abstraction: CLI (one-line install + browser-pair), MCP server (drop into any MCP-compatible runtime), TypeScript SDK (typed code), raw HTTP curl. Each path has working code that runs as-is. An agent that reads top-down hits actionable code within the first 80 lines.

The big mindset shift here was treating `SKILL.md` as the front door, not as a supplement. Old DX put the front door at `/docs` and made you click through three sub-pages to reach the API reference. Agent DX puts everything an agent needs to start writing correct code — auth, install, hello-world, capability map — in the first screen of a single file. `SKILL.md` is that file.

We also kept the references *under* `SKILL.md`. Three deep-dive docs (`linkedin.md`, `webhooks.md`, `websockets.md`) live in `/skills/myagentmail/references/`. The skill manifest links to them by name. An agent reading `SKILL.md` sees a clear pointer: *"For LinkedIn signals, see references/linkedin.md."* No hidden depth, no surprise nesting.

## 4. Ship an MCP server *and* a CLI alongside the SDK

For purely conceptual reasons we resisted this for a while. If the OpenAPI spec is good and the SDK is typed, why does anyone need additional interfaces? Because most agents don't write integration code at all anymore. They use tools — and the *kind* of tool depends on where the agent is running.

### MCP for tool-using runtimes

When an agent runs in Claude Desktop, Cursor, Windsurf, Cline, or any other MCP-compatible runtime, the path of least resistance is *"call a tool the runtime makes available"*. If you ship an MCP server, the agent gets your full API surface as a list of typed function calls — no installation, no auth wiring, no error handling code, no JSON-schema introspection. The agent says *"send an email"* and your `send_message` tool runs.

Our MCP server (`myagentmail-mcp` on npm) exposes ~25 tools spanning every product surface. The user adds one entry to their MCP config:

```json
{
  "mcpServers": {
    "myagentmail": {
      "command": "npx",
      "args": ["-y", "myagentmail-mcp"],
      "env": { "MYAGENTMAIL_API_KEY": "tk_..." }
    }
  }
}
```

That's the full integration. An agent can immediately provision an inbox, send mail, listen for replies, create LinkedIn signals, draft and send connection requests, all without our SDK ever being installed. For agents running in MCP-aware runtimes, this is the default integration path.

### CLI for long-session terminal agents

There's a second pattern MCP doesn't cover well: agents running in terminal-shaped sessions. Claude Code, OpenAI Codex, Aider, agent loops in CI, headless coding agents in cloud sandboxes — these don't have an MCP host wrapping them. They have a shell. They discover capabilities by reading `--help`.

For that population, MCP's strength becomes a liability. Loading 25 tool schemas into the agent's context on every turn is fine for a chat-shaped UI where each turn is one user request; it's expensive for a coding agent that lives in the same context for hours. A CLI inverts that. The agent sees the surface lazily — `myagentmail --help` lists the verb groups, `myagentmail inbox --help` drills in, `myagentmail inbox send --help` shows flags — paying tokens only for the part of the API it actually needs that turn.

The Scalekit team made this observation cleanly in [MCP vs CLI for AI agent integrations](https://www.scalekit.com/blog/mcp-vs-cli-use). Both tools, one tradeoff: MCP front-loads the surface so the runtime can show users what's available; CLIs back-load it so long-running agents pay the discovery cost only when they actually invoke. Most products will need both.

Our CLI (`myagentmail-cli` on npm, binaries `myagentmail` and `mam`) covers the same ~25 surfaces as the MCP server, plus a few terminal-native ones MCP can't easily express — `listen` for ngrok-free webhook tunneling to a local port, `doctor` for a one-shot health check, `mcp install` that auto-detects every MCP host on the machine and wires them up so a user never has to JSON-edit a config.

The auth flow is the part we're proudest of. Most CLIs ask the user to paste an API key. We didn't want to. The default `myagentmail login` opens the user's default browser to a pairing URL, the dashboard shows an Approve button (already authenticated), and the CLI receives the key over a short-lived pairing channel and writes it to `~/.config/myagentmail/credentials` at mode 0600. The key never appears in the browser tab, never appears in shell history, never gets committed to a dotfile by accident. It's the Stripe CLI flow, applied to a category that historically didn't bother. Headless paths still exist (`--api-key tk_...` for CI, `--paste` for SSH-only terminals, `MYAGENTMAIL_KEY` env var for everything), but the human flow leads with browser pairing — and so does the agent flow when running interactively.

### The SDK still matters

There's a meaningful population of agents writing TypeScript or Python code into a customer's repo, and for those the typed surface beats the schema-only surface every time. But shipping *only* the SDK in 2026 is missing the bigger pattern: agents in tool-using runtimes are growing faster than agents writing code from scratch — and within that population, the split between *MCP-shaped tool calls* and *CLI-shaped shell calls* is roughly 50/50, depending on which runtime the user picked.

## 5. Build the example app like a documentation artifact

This was the second-biggest leverage point after `llms.txt`. We had an outreach-starter repo we'd been maintaining as a *"here's a working app"* showcase. When we audited what agents actually did with it, they treated it as documentation: they read it, paraphrased it, and used its patterns to write the customer's app. They almost never forked it.

Once we understood that, we redesigned the repo to optimize for *being read*, not for being run. Concretely:

- **Heavy file-level comments explaining the *why*** of every architectural decision. The webhook handler doesn't just dispatch on event type; the comment block above it explains why MyAgentMail emits three event types and what changes per kind.
- **Single coherent flow** — one onboarding wizard, one leads queue, one outreach surface — rather than three half-built features. Agents copy the patterns they see; if the example has incoherent shapes, the customer's app will too.
- **Short, opinionated file names** — `engagement-runner.ts`, `linkedin-client.ts`, `agent.ts`. An agent grepping for `engagement` finds the right file; an agent grepping for `linkedin-content-search-pipeline-v2-experimental` does not.
- **Comments that name the pitfalls.** When we hit a bug where LinkedIn's GraphQL parser rejects unencoded parens, the fix landed with a code comment naming the bug. An agent reading that file later writes their own code with the same defense, because the comment surfaces the constraint.

The starter is now ~3,500 lines of TypeScript, all of it intentionally read-friendly. The right outcome for an example app in 2026 is "the agent referenced your patterns and produced something that worked", not "the customer cloned the repo unchanged."

## 6. Write your error messages for the LLM that's about to read them

The single subtlest change. Most APIs return errors in the shape `{ "error": "Bad Request" }` or, slightly better, `{ "code": "VALIDATION_ERROR", "error": "Invalid request body" }`. These are fine for humans and useless for agents. An agent receiving *"Bad Request"* has no path forward — it has to either guess what went wrong or escalate to a human.

We rewrote our error responses around three principles:

1. **Name the specific field that's wrong.** Not *"Validation failed"* — *"`query` is required when `kind` is `keyword`."*
2. **Tell the caller what to do next.** Not *"NO_SESSION_AVAILABLE"* — *"All connected LinkedIn sessions are rate-limited or out of search budget for today. Polling resumes automatically when they cool down. Connect another LinkedIn account to add headroom — every additional account multiplies your daily quota."*
3. **Don't leak internal mechanics.** When our parser broke once and we shipped an error message that read *"LinkedIn parser may need attention (try setting `LINKEDIN_PROFILE_POSTS_QUERY_ID` / `LINKEDIN_COMPANY_POSTS_QUERY_ID`)"*, customers' agents helpfully relayed our internal env-var names back to their human operators, who were understandably confused. Now we log the rich detail server-side and surface a customer-safe message.

The rule of thumb we landed on: *write every error response as if a senior engineer were going to read it cold, in a Slack screenshot, with no other context.* What field is wrong? What should I change? Should I retry? Answer those three questions in every error envelope and an agent can correct course on its own.

## 7. Optimize keyword and parameter shapes for fuzzy retrieval

Agents don't read your docs the way humans do. A human scanning your docs for *"email send"* tolerates phrasing like *"deliver an outbound message"*. An agent doing semantic retrieval against your docs may rank `deliver_outbound_message` lower than `send_email` for the same intent, depending on which words appear in the query and what's in the embedding space.

Two small things that compound:

- **Use the words your customers will use.** We renamed several internal endpoint paths and field names to match the verbs that show up in plain customer questions. *"Send a message"* now maps directly to `POST /v1/inboxes/:id/send`. *"Reply to a message"* maps to `POST /v1/inboxes/:id/reply/:msgId`. The grammar of the API mirrors the grammar of the customer's request.
- **Repeat key phrases in descriptions.** Our OpenAPI descriptions are written with deliberate keyword density. The endpoint that creates a signal mentions *"signal"*, *"intent"*, *"webhook"*, *"firing rule"*, and *"LinkedIn"* in its first paragraph. We resist the temptation to write elegant prose with synonyms. We're optimizing for retrieval, not literary merit.

Neither of these is rocket science, but together they affect whether an agent hits the right endpoint on the first try or wastes three turns.

## What didn't work

For honesty's sake: a few things we tried and walked back.

- **A `.well-known/ai-plugin.json` in the OpenAI plugin format.** The spec is effectively dead since OpenAI moved GPTs to a different convention; we shipped one and removed it. `llms.txt` is the live convention.
- **A "try it in your browser" interactive playground built on the OpenAPI spec.** Humans love it. Agents don't see it. Effort better spent elsewhere — in 2026, agents handle the integration and don't need to kick tires. A single one-shot "Test your API key" widget at the top of `/docs` does more useful work than a full playground ever did, with a fraction of the engineering cost.
- **A documentation chatbot.** We considered building a *"MyAgentMail Docs Bot"* the customer could ask questions of. Then we realized the customer already has Claude / Cursor / Windsurf and our job is to make our docs feed those well. Adding our own thin chatbot was duplicative and didn't compound.
- **A third-party docs renderer for the API reference.** We shipped Scalar first; it looked great, but it lived in an iframe outside our design system, didn't expose markdown alternates per endpoint, and didn't let us add the AI-actions toolbar we wanted. Replaced with a custom renderer in a couple of hours of work — not because Scalar is bad (it isn't), but because the agent-readable affordances we wanted weren't its product surface.

## The principle behind all of it

The unifying frame is something like: **agent experience is developer experience optimized for a different reader**. The reader is faster, less curious about your story, more vulnerable to ambiguity, and far less forgiving of out-of-band context. Every page on your developer site is now read by both populations, but agent reading is increasingly the dominant one and growing faster. The good news is that almost everything you do for agents — clearer language, consistent shapes, denser top-of-page actionable content, honest error messages, working examples, paired markdown URLs — also makes the human DX better.

We're not arguing against humans-as-customers. We're arguing that designing primarily for agents *as the actual reader at integration time* produces better DX for both populations. The artifacts that survived this redesign — `/llms.txt`, the paired HTML+markdown API reference, `SKILL.md` as the front door, the MCP server, the CLI with browser pairing, the read-friendly starter, blunt error messages — are the things our human customers like best too. The artifacts that didn't — the playground, the docs chatbot, the third-party iframe — were optimizing for a reader that was already a minority and falling.

If you're maintaining a developer product in 2026 and you haven't yet treated agents as a first-class integration audience, this is the work. There's no exotic infrastructure required. It's mostly editorial discipline applied to docs and SDKs you probably already have. The dividend is integrations that ship in minutes instead of days, and a customer support load that compresses because agents stop asking the questions whose answers are already in your docs.

The role of the developer experience PM isn't going away. It's just been renamed.

## Resources

- **MyAgentMail's `/llms.txt`** — [myagentmail.com/llms.txt](https://myagentmail.com/llms.txt). Reverse-engineer it, copy the structure.
- **MyAgentMail's `SKILL.md`** — [myagentmail.com/skills/myagentmail/SKILL.md](https://myagentmail.com/skills/myagentmail/SKILL.md). Note the front-loaded Quick Start.
- **MyAgentMail's API reference** — [myagentmail.com/docs](https://myagentmail.com/docs). Paired HTML + markdown for every endpoint. Try [`/docs/domains/listdomains`](https://myagentmail.com/docs/domains/listdomains) and its markdown sibling [`/docs-md/domains/listdomains`](https://myagentmail.com/docs-md/domains/listdomains).
- **The `llms.txt` spec** — [llmstxt.org](https://llmstxt.org).
- **Anthropic's skills repo** — [github.com/anthropics/skills](https://github.com/anthropics/skills) for the `SKILL.md` convention.
- **MCP servers worth reading as model implementations** — Anthropic, GitHub, Stripe, and Linear all ship reference servers.
- **CLI patterns to copy** — Stripe CLI's `stripe login` (the Stripe-style browser pairing that ours mirrors), Vercel CLI's `vc env pull` (auth state portable across machines), GitHub CLI's `gh auth login` (multiple host support).
- **MCP vs CLI tradeoffs** — [Scalekit on MCP vs CLI for AI agent integrations](https://www.scalekit.com/blog/mcp-vs-cli-use). Same observation we landed on independently: tool-runtime agents want MCP, terminal-shaped long-session agents want a CLI.
- **The MyAgentMail outreach starter** — [github.com/kamskans/myagentmail-outreach-starter](https://github.com/kamskans/myagentmail-outreach-starter). The example app, redesigned around being read.

---

*Building a developer product and want to think through your own DX-for-agents strategy? [DM me on LinkedIn](https://www.linkedin.com/in/kamalkannans).*
