
AIにHTMLではなくMarkdownを返す——zench-aine.io を Level 3 へ(Markdownネゴシエーション実践)
はじめに
連載第1回で Cloudflare の Agent Readiness Score(=「AI エージェント版の Lighthouse」)を解説し、第2回で zench-aine.io を実際に直して Level 1 → Level 2 へ上げました。今回はその続きで、Level 2 → Level 3「Agent-Readable」への昇格に挑みます。鍵になるのが Markdown コンテンツネゴシエーションです。
第2回でも触れたとおり、zench-aine.io は Next.js 15(App Router) に MDX ベースのコンテンツ管理ライブラリ Velite を組み合わせて構築しています。今回は実装の比重が大きいので、この前提を最初に共有しておきます。
この記事のポイント
- Markdown コンテンツネゴシエーションとは、
Accept: text/markdownを送るエージェントに HTML でなく Markdown を返す HTTP の仕組み - 診断ツールの 「Copy prompt」 が出す修正手順をそのまま実装の出発点にできる
- Next.js で自前実装し、
zench-aine.ioのスコアが 36 → 43、Level 3「Agent-Readable」に到達した
スキャナーの「Copy prompt」で直し方を受け取る
Agent Readiness Score の親切な点は、不合格チェックごとに 「Copy prompt」ボタンがあることです。押すと、その項目を直すための指示文がクリップボードにコピーされ、Cursor や Claude Code などのコーディングエージェントにそのまま貼り付けられます。

Markdown Negotiation の項目で「Copy prompt」を押すと、次の内容が得られます。
Goal: Return HTML responses as markdown when agents request it
Issue: Site does not support Markdown for Agents
Fix: Enable Markdown for Agents so requests with Accept: text/markdown
return a markdown version of your HTML response while HTML stays the
default for browsers. Confirm the response uses Content-Type:
text/markdown (and x-markdown-tokens if available).
Skill: https://isitagentready.com/.well-known/agent-skills/markdown-negotiation/SKILL.md
Docs: https://developers.cloudflare.com/fundamentals/reference/markdown-for-agents/Goal(目的)・Issue(問題)・Fix(直し方) が簡潔にまとまり、さらに Agent Skills 形式の SKILL.md(エージェント向けの実装手順書)と公式ドキュメントへのリンクまで付いています。この SKILL.md には「Accept: text/markdown のとき Content-Type: text/markdown で返す」「ブラウザには HTML を既定で返す」「可能なら x-markdown-tokens ヘッダーを付ける」といった要件が書かれており、今回はこれに沿って実装しました。
Markdown Negotiation とは? 何が嬉しいのか
Markdown コンテンツネゴシエーションとは、クライアントが Accept: text/markdown というリクエストヘッダーを送ったときだけ、サーバーが冗長な HTML の代わりに整形済み Markdown を返す HTTP の仕組みです。普通のブラウザ(HTML を要求)には従来どおり HTML を返すので、人間の閲覧体験は変わりません。
AI エージェントにとっての利点は大きく2つあります。
- トークンの大幅削減 — ナビゲーション・広告・装飾的な HTML タグが消え、本文だけの Markdown になります。Cloudflare の実例では 12,345 トークンの HTML が 725 トークンの Markdown に(約94%減)。AI はより多くの記事を、より少ないコンテキストとコストで読めます。
- 誤読の減少 — 構造がはっきりした Markdown は、AI にとって解析しやすく、本文と装飾の区別で迷いません。結果として理解と引用の精度が上がります。
記事を「正確に・効率よく AI に読まれ、引用される」ことを狙うメディアサイトにとって、これは本質的な対応です。そして Agent Readiness Score では、これが Level 3「Agent-Readable」のゲートになっています。
DNS-AID はなぜ見送るのか?
同じ不合格項目でも、DNS for AI Discovery(DNS-AID)は今回あえて見送りました。判断の理由を書いておきます。
DNS-AID とは、AI エージェントや MCP サーバーの「エンドポイント」を DNS(SVCB / TLSA / DNSSEC など)に公開し、他のエージェントが第三者レジストリを介さず発見・検証できるようにする仕組みです。つまり、エージェントやサービスを“提供する側”の組織向けの機能です。
zench-aine.io は記事を読ませるメディアサイトで、DNS で公開すべきエージェント/MCP エンドポイントを持っていません。IETF/Cloudflare の説明でも「エージェントエンドポイントを持たないコンテンツ・メディアサイトには DNS-AID の実装は不要」とされています。加えて、まだ正式な標準ではない Internet-Draft の段階で、DNSSEC などの運用負荷も伴います。スコアのために実装しても、使われない設定が増えるだけ——だから見送りが正解だと判断しました。
Next.js で Markdown Negotiation を実装する
zench-aine.io は Cloudflare のプロキシを経由していない(Cloud Run 直)ため、ダッシュボードの「Markdown for Agents」機能は使えません。そこで Next.js 側で自前実装します。方針は「middleware で Accept: text/markdown を検知し、Markdown を返す Route Handler へ転送する」です。
まず middleware で振り分けます。
// middleware.ts
// text/markdown を q>0 で受理しているときだけ true(q=0 の明示拒否を除外)
function acceptsMarkdown(accept: string): boolean {
for (const part of accept.split(',')) {
const tokens = part.trim().toLowerCase().split(';').map((s) => s.trim())
if (tokens[0] !== 'text/markdown') continue
const qToken = tokens.find((t) => t.startsWith('q='))
const q = qToken ? Number.parseFloat(qToken.slice(2)) : 1
if (!Number.isNaN(q) && q > 0) return true
}
return false
}
export default function middleware(request: NextRequest) {
const accept = request.headers.get('accept') || ''
if (acceptsMarkdown(accept)) {
const url = request.nextUrl.clone()
url.pathname = `/api/markdown${url.pathname === '/' ? '' : url.pathname}`
return NextResponse.rewrite(url)
}
return intlMiddleware(request) // 通常は next-intl のルーティング
}Accept: text/markdown を単純に部分一致で拾うと、Accept: text/markdown;q=0(=「Markdown は要らない」という明示的な拒否)まで誤って拾ってしまいます。上のように q 値を見て q>0 のときだけ転送するのが、HTTP コンテンツネゴシエーションとして正確です。
転送先の Route Handler は、ホームページ・記事・一覧それぞれの Markdown を返します。記事本文は Velite のスキーマに raw: s.raw() を足して生の Markdown を取得し、frontmatter を付けて返します。また、/media/{slug}/余分なパス や存在しないページには、HTML 側と挙動を揃えて 404 を返すようにします(Markdown だけ URL 境界が緩くならないように)。
// app/api/markdown/[[...path]]/route.ts(抜粋)
function markdownResponse(body: string) {
return new Response(body, {
headers: {
'Content-Type': 'text/markdown; charset=utf-8',
'x-markdown-tokens': String(Math.ceil(body.length / 4)), // 概算トークン数
'Vary': 'Accept',
},
})
}ポイントは SKILL.md の要件に合わせ、Content-Type: text/markdown と x-markdown-tokens を返し、Vary: Accept でキャッシュを正しく分けることです。ブラウザ(Accept: text/html)には従来の HTML をそのまま返すので、人間の体験は一切変わりません。
ローカルで確認すると、curl -H "Accept: text/markdown" https://zench-aine.io/ が Markdown を、通常のブラウザアクセスが HTML を返す、という期待どおりの挙動になりました。
再スキャンの結果は? Level 3 へ
本番にデプロイして再スキャンした結果、Content カテゴリが 1/1(Markdown Negotiation が pass)になり、スコアは 36 → 43 に上昇しました。

スキャン API 上のレベルは、第2回で予告したとおり Level 3「Agent-Readable」 に到達しました。
ひとつ正直に補足します。isitagentready の画面バッジは「Level 4 Agent-Integrated」と表示されますが、スキャン API が返す実際のレベルは Level 3「Agent-Readable」 です。Level 4「Agent-Integrated」の要件は MCP サーバーや Agent Skills など(API/Auth/MCP カテゴリは 0/7)で、私たちはコンテンツサイトとしてこれらを意図的に実装していません。したがって実体は「Agent-Readable」です。スコアやバッジを鵜呑みにせず、中身で判断するのが大切、という良い例でもあります。
次の Level 4「Agent-Integrated」のゲートは、まさにこの MCP・A2A・Agent Skills・API カタログ——前回・今回と一貫して「コンテンツサイトには不要」と説明してきた、サービス提供側向けの項目です。つまり zench-aine.io にとっては、Level 3「Agent-Readable」が当面の実質的なゴールと言えます。
よくある質問
Q. Markdown を返すと、人間の見た目は変わりますか?
A. 変わりません。Accept: text/markdown を明示的に送るエージェントにだけ Markdown を返し、ブラウザ(Accept: text/html)には従来どおり HTML を返します。Vary: Accept を付けてキャッシュも分離するため、人間の閲覧体験は一切影響を受けません。
Q. なぜ Cloudflare の機能を使わなかったのですか?
A. zench-aine.io が Cloudflare のプロキシを経由していない(Cloud Run 直)ためです。プロキシ経由ならダッシュボードのトグルだけで有効化できますが、今回は構成上使えないので Next.js 側で自前実装しました。
Q. llms.txt があれば Markdown ネゴシエーションは不要では?
A. 役割が違います。llms.txt は「サイト全体の目次」、Markdown ネゴシエーションは「個々のページ本文を Markdown で返す」仕組みです。前者で記事を発見させ、後者で本文を効率よく読ませる、という補完関係にあります。
まとめ
Markdown コンテンツネゴシエーションは、Accept: text/markdown を送る AI エージェントに整形済み Markdown を返し、トークンを大幅に削減しつつ本文の理解・引用精度を高める仕組みです。zench-aine.io は診断ツールの「Copy prompt」を出発点に Next.js で自前実装し、スコア 36 → 43、Level 3「Agent-Readable」に到達しました。
一方で DNS-AID や Level 4 の MCP/Agent Skills は、サービス提供側向けの項目としてあえて見送りました。スコアを追うのではなく、自社の性格に合った項目を見極めて対応する——連載を通じて一貫しているこの姿勢が、結局いちばん効きます。
第1回(解説・考察編)、第2回(Level 1→2 実践編)とあわせて、AI エージェント時代の Web 標準対応の地図として役立てば幸いです。ZenChAIne では、こうした取り組みを実装の手触りごと発信していきます。