How to serve documentation for agents

Learn how to serve markdown to agents and HTML for humans from the same URL

2 min read
Last updated January 14, 2026

Content negotiation allows clients to request different representations of the same resource using the HTTP-standard Accept header, rather than requiring different URLs.

For example, the Vercel documentation uses this technique to respond with markdown when an agent requests docs:

Terminal
# Responds with HTML
curl https://vercel.com/docs
# Responds with markdown
curl -H "Accept: text/markdown" https://vercel.com/docs

This guide assumes that your content is authored using markdown in a Next.js app, but can be adapted to your content authoring strategy and framework by converting your content to markdown as it is requested.

First, create a Route Handler that returns markdown responses when it is requested:

app/docs/md/[[...slug]]/route.ts
import { notFound } from 'next/navigation';
import { getMarkdownContent } from '@/lib/content';
export async function GET(
_req: Request,
{ params }: { params: Promise<{ slug?: string[] }> }
) {
const { slug } = await params;
const content = getMarkdownContent(slug?.join('/') ?? 'index');
if (!content) {
notFound();
}
return new Response(content, {
headers: {
'Content-Type': 'text/markdown',
},
});
}

Add a function to your proxy.ts that handles the Accept header:

proxy.ts
import { NextRequest, NextResponse } from 'next/server';
function acceptsMarkdown(request: NextRequest): boolean {
const acceptHeader = request.headers.get('accept') || '';
return (
acceptHeader.includes('text/markdown') ||
acceptHeader.includes('text/x-markdown')
)
}

Then, use the function to rewrite response to the Route Handler you created previously:

export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
if (pathname.startsWith('/docs/') && acceptsMarkdown(request)) {
// Rewrite to your markdown route
const slug = pathname.replace('/docs/', '');
return NextResponse.rewrite(new URL(`/docs/md/${slug}`)), request.url));
}
return NextResponse.next();
}
export const config = {
matcher: '/docs/:path*}',
};

You can now use curl to receive different response types of your content.

Terminal
# Responds with HTML
curl https://your-domain.com/docs
# Responds with markdown
curl -H "Accept: text/markdown" https://your-domain.com/docs

Was this helpful?

supported.

Read related documentation

No related documentation available.

Explore more guides

No related guides available.

How to serve documentation for agents | Vercel Knowledge Base