Skip to content

Content Negotiation for AI Agents: Why Sentry Serves Markdown Over HTML

• 9 min read
Bart Waardenburg

Bart Waardenburg

AI Agent Readiness Expert & Founder

David Cramer, co-founder and CPO of Sentry, recently published a practical guide to optimizing content for AI agents . His argument: "you should be optimizing content for agents, just as you optimize things for people." His method needs no new protocol. It uses HTTP content negotiation, a standard that has been in the specification since 1999.

When a request includes Accept: text/markdown, the server returns clean markdown instead of a full HTML page. Navigation, JavaScript and sidebar widgets are stripped, and the agent receives only the content.

What Is Content Negotiation?

Content negotiation is an HTTP mechanism. The client tells the server which format it prefers, and the server responds in that format. It is defined in RFC 9110 and has been part of the web since HTTP/1.1. Browsers have always used it. When your browser sends Accept: text/html, the server knows to return HTML.

AI agents can use the same mechanism. When an agent sends Accept: text/markdown, the server can return a stripped-down markdown version of the page. The URL and the content stay the same, and only the representation changes. The server needs no separate API and no special endpoints.

Agent request with content negotiation plain
GET /docs/getting-started HTTP/1.1
Host: docs.example.com
Accept: text/markdown, text/html;q=0.9

---

HTTP/1.1 200 OK
Content-Type: text/markdown; charset=utf-8
Vary: Accept

# Getting Started

Install the SDK with your package manager:

```bash
npm install @example/sdk
```

The Three Optimization Areas

Cramer names three areas where content optimization matters most for agents. He bases them on how frontier models process web content:

Order of Content

Agents read sequentially from the top. If your key information is buried below navigation, breadcrumbs, and sidebar HTML, the agent may never reach it, or may waste tokens getting there.

Content Size

Frontier models often read only the initial portion of large pages to manage context windows. Smaller, focused content means the agent gets the full picture instead of a truncated one.

Depth of Nodes

Deeply nested HTML, with divs wrapping divs wrapping divs, adds tokens without adding meaning. Flat markdown preserves the same semantics with a fraction of the markup.

Together, these explain why serving HTML to an agent wastes tokens. A documentation page might be 15,000 tokens as HTML and 3,000 tokens as markdown. The markdown keeps the headings, paragraphs, code blocks and links, and drops the structural overhead.

How Sentry Implements Content Negotiation

Sentry's approach has three layers. Each one solves a different problem.

1. Documentation: Serve True Markdown

When an agent requests Sentry's documentation with Accept: text/markdown, the server returns the raw MDX source. This is the content that generates the HTML page, without the rendered navigation, JavaScript bundles or UI chrome. Sentry also restructures the link hierarchy, so agents find related pages through a flat sitemap and do not have to crawl nested navigation trees.

Of the three layers, this one applies to the most sites. Any site with documentation, help articles or structured content can detect the Accept header and return a stripped representation.

2. Authenticated Pages: Redirect to Programmatic Alternatives

When an agent requests an authenticated page in the Sentry web UI, such as a dashboard, an issue detail or project settings, Sentry does not return a login wall. It redirects the agent to a programmatic alternative: the MCP server, the CLI tool or the REST API. The agent learns where it can access the data.

3. Bootstrap Discovery: Let Agents Self-Configure

For Sentry's CLI tools and integrations, an agent can fetch a markdown document that describes the purpose, capabilities and quick-start steps of the tool. The agent learns what the tool does and how to use it without parsing HTML documentation pages.

This is the same principle behind llms.txt . That file gives agents a curated, machine-readable entry point. Content negotiation controls the format of a page, and llms.txt controls which pages the agent reads first.

Who's Already Doing This?

Other companies also use content negotiation with agents:

Sentry

Content negotiation across docs, authenticated pages and tool bootstrapping.

Vercel

Built AEO tracking for coding agents and recommends content negotiation in its State of AEO report.

Cloudflare

Offers markdown transformation and content negotiation as a built-in feature in its paid tiers.

Coding Agents

Claude Code, Cursor, Windsurf and Bun send Accept headers that prefer markdown when they fetch documentation pages.

Both sides are moving. Some content providers now serve markdown, and some AI agents now request it.

How to Implement Content Negotiation

The implementation takes three steps.

Step 1: Detect Agent Requests

Check the Accept header for text/markdown. Compare the q-values instead of searching for the string, so that a browser that sends */* still gets HTML. Also set Vary: Accept on every response:

Plug-based content negotiation (Elixir/Phoenix) elixir
defmodule MyAppWeb.Plugs.ContentNegotiation do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    conn
    |> put_resp_header("vary", "accept")
    |> assign(:prefer_markdown, prefers_markdown?(get_req_header(conn, "accept")))
  end

  # Markdown wins only when it has the highest q-value.
  # A browser that sends */* keeps getting HTML.
  defp prefers_markdown?(accept_headers) do
    preferred =
      accept_headers
      |> Enum.flat_map(&String.split(&1, ","))
      |> Enum.map(&parse_media_range/1)
      |> Enum.max_by(fn {_type, q} -> q end, fn -> {"*/*", 1.0} end)

    match?({"text/markdown", q} when q > 0, preferred)
  end

  defp parse_media_range(range) do
    [type | params] = range |> String.split(";") |> Enum.map(&String.trim/1)

    q =
      Enum.find_value(params, 1.0, fn
        "q=" <> value ->
          case Float.parse(value) do
            {q, _rest} -> q
            :error -> 0.0
          end

        _param ->
          nil
      end)

    {String.downcase(type), q}
  end
end
Express middleware (Node.js) javascript
// List text/html first: for Accept: */* Express picks the first type,
// so browsers and plain curl keep getting HTML.
function contentNegotiation(req, res, next) {
  res.vary('Accept');
  req.prefersMarkdown = req.accepts(['text/html', 'text/markdown'])
    === 'text/markdown';
  next();
}

app.get('/docs/:slug', contentNegotiation, (req, res) => {
  if (req.prefersMarkdown) {
    res.type('text/markdown').send(getMarkdownContent(req.params.slug));
  } else {
    res.render('docs', { slug: req.params.slug });
  }
});

Step 2: Serve Clean Markdown

Strip everything that isn't content: navigation, headers, footers, JavaScript, tracking pixels, cookie banners. What remains should be the same content that appears in the rendered page, but as clean markdown with proper heading hierarchy.

  • Do include: headings, paragraphs, code blocks, links, lists, images with alt text, tables
  • Don't include: navigation HTML, script tags, style blocks, tracking pixels, UI framework markup
  • Do set: Content-Type: text/markdown and Vary: Accept headers

The Vary: Accept header tells caches and CDNs that the response depends on the Accept header. Without it, a cache can serve a cached markdown response to a browser.

Test both representations with curl. The first request must return Content-Type: text/markdown, the second must return HTML, and both must include Vary: Accept:

Check the negotiation with curl plain
curl -sI -H "Accept: text/markdown" https://docs.example.com/docs/getting-started
curl -sI https://docs.example.com/docs/getting-started

Step 3: Front-Load Key Information

Agents read sequentially and may truncate long content, so put the most important information first:

  • Start with a clear title and one-paragraph summary
  • Put installation/quick-start instructions near the top
  • Move prerequisites, background context, and changelog to the bottom
  • Use descriptive headings that make sense without reading the content below them

Beyond Developer Docs

Content negotiation started in developer documentation, but the same approach fits other content-heavy websites. Some examples:

Website Type What to Serve as Markdown Agent Use Case
Developer docs API references, guides, tutorials Coding agents fetching SDK usage examples
E-commerce Product specs, comparisons, reviews Shopping agents comparing products for users
SaaS help centers Support articles, FAQs, setup guides Customer support agents answering questions
News/media Article content, structured data Research agents gathering information
Government/legal Regulations, forms, procedures Compliance agents checking requirements

As AI agents take on browsing, shopping and research tasks, more websites will receive agent requests. Those sites can choose to serve the content as markdown or as full HTML.

How This Fits into AI Agent Readiness

Content negotiation is one part of agent readiness. It works alongside the other signals agents look for:

llms.txt

llms.txt points agents to the pages that matter most. Content negotiation controls how those pages are served. Together, they give agents a short path to your best content.

Structured Data

JSON-LD and Schema.org give agents context about your content: what it is, who wrote it and when it was updated. Markdown gives them the text itself.

robots.txt & Crawler Access

Agents need permission to access your content. An open robots.txt lets them fetch it, and content negotiation lets them read it with fewer tokens.

Agent Protocols

WebMCP, MCP and A2A let agents take actions. Content negotiation lets them read.

The Bottom Line

David Cramer's post describes agent optimization with an existing tool: content negotiation, a 25-year-old HTTP standard, applied to agent requests.

To implement it, detect Accept: text/markdown, return clean markdown and put your key content first. For authenticated pages, redirect agents to APIs and CLIs instead of login walls. For discovery, provide a curated entry point with llms.txt .

Sentry, Vercel and Cloudflare build for agents with standards that already exist. The main obstacle is that few teams know about the mechanism.

Sources

Ready to check?

Scan your website

Measure published signals and get recommendations across 5 categories. The stable-core score and methodology grade appear only when evidence is complete.

  • Stable-core score and grade when evidence is complete
  • 5 categories, 77 checkpoints
  • Code examples for every recommendation

Related articles

Continue reading about AI agent readiness and web optimization.

Does Schema Markup Get You Cited by AI? What the Data Actually Shows
• 9 min read

Does Schema Markup Get You Cited by AI? What the Data Actually Shows

A 2026 Ahrefs study tracked 1,885 pages adding schema markup. AI-search citations barely moved. We cover what structured data proves and what drives citations.

ai-agents seo getting-started
Cloudflare /crawl Endpoint: One API Call to Crawl Any Website
• 9 min read

Cloudflare /crawl Endpoint: One API Call to Crawl Any Website

Cloudflare's /crawl endpoint crawls a whole website with one API call and returns HTML, Markdown or JSON. We cover robots.txt, sitemaps and SSR.

ai-agents seo getting-started
AI Crawlers Ignore llms.txt, But AI Agents Don't
• 9 min read

AI Crawlers Ignore llms.txt, But AI Agents Don't

Dries Buytaert's data shows no AI crawler uses llms.txt. Crawlers scrape training data, while agents complete tasks. We cover which coding agents use llms.txt.

ai-agents seo getting-started

Explore more

Explore the checks behind measured stable-core coverage.

Rankings
SEE HOW OTHERS SCORE

Rankings

Browse AI readiness scores for scanned websites.
Compare
HEAD TO HEAD

Compare

Compare two websites side-by-side across all 5 weighted categories.
About
HOW WE MEASURE

About

Learn about our 5-category scoring methodology.