If you've been following developer trends recently, you might have seen discussions about serving Markdown directly to AI coding assistants like Claude Code, Cursor, and GitHub Copilot. Companies like Bun, Mintlify, and Fumadocs have already implemented this feature in their documentation platforms.
Here's why it matters for your deployment documentation and how to implement it yourself with Laravel.
The Problem: HTML Bloat
AI agents can fetch the content of web pages to help developers. Most of the time, this content comes back as HTML. But HTML contains markup that consumes tokens without adding meaningful information, degrading performance.
For example, a simple documentation page about deployment commands might look like this in HTML:
<div class="documentation-wrapper">
<header class="docs-header">...</header>
<nav class="sidebar">...</nav>
<main class="content">
<h1 class="heading-primary">Deployment Commands</h1>
<p class="text-body">To deploy your application, use the following command:</p>
<pre class="code-block"><code>deployhq deploy production</code></pre>
</main>
<footer class="docs-footer">...</footer>
</div>
The same content in Markdown is much cleaner:
# Deployment Commands
To deploy your application, use the following command:
deployhq deploy production
The Solution: Content Negotiation
The elegant solution is content negotiation. This is a standard HTTP mechanism where clients tell servers what format they prefer using the Accept header.
When an AI agent requests your documentation with Accept: text/markdown or Accept: text/plain, your server can respond with Markdown instead of HTML.
This means:
- Fewer tokens for the same information
- Easier parsing and comprehension
- More documentation fits in the context window
Implementation Guide for Laravel
Let's walk through implementing this feature in a Laravel application. The exact details depend on your setup, but the general approach remains the same.
1. Detect the Accept Header
When a request arrives, check if the Accept header contains text/markdown or text/plain:
// routes/web.php
Route::get('/docs/{path?}', function (Request $request, $path = 'index') {
$acceptHeader = $request->header('Accept', '');
if (str_contains($acceptHeader, 'text/markdown')) {
// Serve Markdown
} elseif (str_contains($acceptHeader, 'text/plain')) {
// Serve plain text
} else {
// Serve HTML (default behavior)
}
})->where('path', '.*');
2. Serve the Raw Markdown
Load and return the raw Markdown content for the requested page:
Route::get('/docs/{path?}', function (Request $request, $path = 'index') {
$acceptHeader = $request->header('Accept', '');
if (str_contains($acceptHeader, 'text/markdown')) {
$markdownContent = loadMarkdownForPage($path);
return response($markdownContent)
->header('Content-Type', 'text/markdown; charset=utf-8');
}
// ... rest of implementation
})->where('path', '.*');
3. Create a Controller for Better Organization
For a cleaner approach, create a dedicated controller:
// app/Http/Controllers/DocsController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
class DocsController extends Controller
{
public function show(Request $request, $path = 'index')
{
$acceptHeader = $request->header('Accept', '');
$markdownContent = $this->loadMarkdown($path);
if (!$markdownContent) {
abort(404, 'Documentation not found');
}
if (str_contains($acceptHeader, 'text/markdown')) {
return response($markdownContent)
->header('Content-Type', 'text/markdown; charset=utf-8');
}
if (str_contains($acceptHeader, 'text/plain')) {
return response($markdownContent)
->header('Content-Type', 'text/plain; charset=utf-8');
}
// Default: Serve HTML
return view('docs.show', [
'content' => Str::markdown($markdownContent)
]);
}
private function loadMarkdown($path)
{
// Sanitize path to prevent directory traversal
$cleanPath = str_replace(['../', './'], '', $path);
$filePath = resource_path("docs/{$cleanPath}.md");
if (!File::exists($filePath)) {
return null;
}
return File::get($filePath);
}
}
4. Configure Your Routes
// routes/web.php
use App\Http\Controllers\DocsController;
Route::get('/docs/{path?}', [DocsController::class, 'show'])
->where('path', '.*')
->name('docs.show');
Testing Your Implementation
Use curl to verify your implementation works correctly:
# Request Markdown
curl -H 'Accept: text/markdown' https://deployhq.com/guides/laravel
# Request plain text
curl -H 'Accept: text/plain' https://deployhq.com/guides/laravel
# Request HTML (default)
curl -H 'Accept: text/html' https://deployhq.com/guides/laravel
For Markdown requests, you should see:
- Response header:
Content-Type: text/markdown; charset=utf-8 - Body: Raw Markdown content without HTML tags
For HTML requests, you should see your normal rendered page.
Adding Caching for Better Performance
To improve performance, cache both Markdown and HTML versions:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
class DocsController extends Controller
{
public function show(Request $request, $path = 'index')
{
$acceptHeader = $request->header('Accept', '');
$contentType = $this->getContentType($acceptHeader);
$cacheKey = "docs.{$path}.{$contentType}";
$content = Cache::remember($cacheKey, 3600, function () use ($path, $contentType) {
$markdown = $this->loadMarkdown($path);
if (!$markdown) {
return null;
}
return $contentType === 'html'
? Str::markdown($markdown)
: $markdown;
});
if (!$content) {
abort(404, 'Documentation not found');
}
if ($contentType === 'markdown') {
return response($content)
->header('Content-Type', 'text/markdown; charset=utf-8');
}
if ($contentType === 'text') {
return response($content)
->header('Content-Type', 'text/plain; charset=utf-8');
}
return view('docs.show', ['content' => $content]);
}
private function getContentType($acceptHeader)
{
if (str_contains($acceptHeader, 'text/markdown')) {
return 'markdown';
}
if (str_contains($acceptHeader, 'text/plain')) {
return 'text';
}
return 'html';
}
private function loadMarkdown($path)
{
$cleanPath = str_replace(['../', './'], '', $path);
$filePath = resource_path("docs/{$cleanPath}.md");
if (!File::exists($filePath)) {
return null;
}
return File::get($filePath);
}
}
Why This Matters for DeployHQ Users
When developers use AI coding assistants like Claude Code or Cursor to help with deployment automation, the AI can now:
- Fetch your deployment documentation more efficiently
- Access more comprehensive guides without hitting token limits
- Better understand complex deployment pipelines
- Provide more accurate assistance with DeployHQ integrations
Whether you're documenting deployment workflows, API endpoints, or configuration options, this simple optimization makes your documentation more accessible to the AI tools developers use every day.
Further Reading and Resources
To learn more about implementing this feature:
- Accept Header Documentation
- Laravel Response Documentation
- Laravel String Helpers (includes Markdown parsing)
Want to automate your deployments? Learn more about DeployHQ and how we help teams deploy faster and more reliably.