Skip to content

fix(agent-score): directive placement, markdown parity, MCP endpoints - #8099

Merged
ankur-arch merged 5 commits into
mainfrom
agent-score-directive-parity-mcp
Jul 24, 2026
Merged

fix(agent-score): directive placement, markdown parity, MCP endpoints#8099
ankur-arch merged 5 commits into
mainfrom
agent-score-directive-parity-mcp

Conversation

@ankur-arch

@ankur-arch ankur-arch commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

TL;DR

The agent-readiness audit (afdocs) flags three things on www.prisma.io/docs. This PR fixes all three and adds CI guards so they stay fixed. Verified locally with the real audit CLI against a production build: all previously failing checks pass.

Check Before Root cause Fix
llms.txt directive in HTML warn — "buried past 50%" Directive rendered inside page content, after the sidebar markup Moved to first child of <body> in the docs layout (now at 0–1%)
Markdown/HTML parity fail — pages 52–90% missing .md pipeline lost headings, broke fences in <details>, and the OpenAPI explorer has no markdown twin Restore heading markers from the toc; convert <details>; data-markdown-ignore on APIPage; unescape \_ \{ in prose
MCP server discoverable fail — nothing at /mcp Checker probes <origin>/mcp with an MCP initialize; /mcp is a marketing page /docs/mcp proxy route to mcp.prisma.io/mcp + header-matched rewrites on site /mcp (browsers still get the page)

Details

Directive. afdocs measures where the llms.txt link sits in the raw HTML body and warns when it's past 50%. It's now a visually-hidden, aria-hidden first child of <body> in apps/docs/src/app/layout.tsx, removed from the per-page component.

Parity. afdocs extracts text segments from the HTML and checks each exists in the .md. Three pipeline bugs made real content vanish from the markdown side:

  1. fumadocs' processed markdown emits headings as bare Text [#anchor] lines — getLLMText now restores ## markers from the page toc, so every .md has real headings (this alone fixed ~7 warning pages with "1. Set up…" step headings).
  2. <details> bodies serialize 2-space indented, which un-fences the code blocks inside — new formatDetails transform emits a bold summary + dedented body.
  3. Management API endpoint pages render an interactive OpenAPI explorer with no markdown equivalent — its wrapper now carries data-markdown-ignore (the audit's sanctioned mechanism); the .md keeps the generated API summary.

MCP. /docs/mcp proxies MCP protocol traffic (POST / SSE GET / DELETE) to the real OAuth-gated server, preserving the WWW-Authenticate challenge; plain browser GETs redirect to the /mcp marketing page. On the site, three header-matched beforeFiles rewrites do the same for www.prisma.io/mcp. Note: the server requires OAuth — if the checker insists on an unauthenticated tool list, a public no-auth server would be a separate product decision.

Guards. lint:agent-ready gains checks for: directive-first-in-<body>, heading markers across all 621 pages, <details> leakage, the APIPage wrapper, and both MCP endpoints. The docs-agent-ready skill documents the invariants and how to reproduce the audit (npx afdocs@0.18.7 check …).

CodeRabbit review (all 5 findings addressed in 97c9b02):

  • directive is aria-hidden with tabIndex={-1} — invisible to screen readers and the tab order
  • MCP proxy rejects bodies > 1 MiB (incl. chunked without Content-Length) and aborts upstream fetch after 30s to headers, SSE streaming preserved
  • guard requires all three /mcp header rewrites, not just one
  • afdocs version pinned in the skill's reproduction commands

Verification

  • lint:agent-ready 26 checks, 0 failures / 0 warnings; test:llm-markdown 9/9; both apps typecheck
  • afdocs CLI vs local production build, on the 20 pages that previously failed/warned: directive pass ("near the top", 0–1%), parity pass (avg 1% missing, was avg 8% with four hard fails), .md URLs + content negotiation 20/20
  • /docs/mcp manually probed: initialize → 401 + WWW-Authenticate (spec-compliant), 2 MiB body → 413, browser GET → 307

After deploy: npx afdocs@0.18.7 check https://www.prisma.io/docs --sampling deterministic -v to confirm the production score.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added an AI-agent-optimized documentation directive in the root HTML to enable consistent access to llms.txt, including Markdown versions via .md URLs.
    • Enabled MCP connectivity with a documentation-side /docs/mcp proxy and site-side /mcp header-based routing for streaming and session traffic.
  • Improvements
    • Enhanced Markdown normalization to preserve heading structure, flatten <details>/<summary> reliably, and protect fenced/inline code for better HTML/Markdown parity.
    • Improved agent-readiness auditing by strengthening directive placement, heading/anchor validation, and MCP endpoint verification.
  • Documentation
    • Updated agent-readiness and playbook guidance with stricter invariants and expanded “reproducing the audit” steps.

Fixes the three regressing checks in the Mintlify/afdocs agent-readiness
audit and adds guards so they stay fixed:

Content Discoverability (directive buried past 50% of the HTML):
- Move the hidden llms.txt directive to the first child of <body> in the
  docs root layout; the sidebar markup previously pushed it to 30-88% of
  the body. Now sits at 0-1% on every page.

Markdown Content Parity (pages failing at 52-90% missing):
- Restore heading markers in getLLMText output from the page toc —
  fumadocs' processed markdown emits headings as bare "Text [#anchor]"
  lines, so "## 1. Set up your project" degraded to a stripped list item
  on the markdown side.
- Convert <details>/<summary> to a bold summary + dedented body; the
  serialized 2-space indent broke the code fences inside.
- Wrap the OpenAPI explorer (APIPage) in data-markdown-ignore; the
  interactive reference has no markdown equivalent and the .md already
  carries the generated API summary.
- Unescape remark escapes (\_, \{, \}) outside code so prose like
  snake_case matches the rendered HTML.

MCP Server Discoverable (no server at expected endpoints):
- New /docs/mcp route proxies MCP protocol traffic to mcp.prisma.io/mcp
  (browser GETs redirect to the /mcp marketing page).
- Header-matched beforeFiles rewrites on the site route MCP traffic on
  www.prisma.io/mcp to the real server; browsers still get the page.

Guards: lint-agent-ready now checks directive-first-in-body, heading
markers across all pages, <details> leakage, the APIPage parity wrapper,
and both MCP endpoints. Skill docs updated with the new invariants and
an afdocs reproduction playbook.

Verified with the afdocs CLI against a local production build: directive
+ parity + markdown-url + content-negotiation all pass on the 20 pages
that previously failed or warned (avg 1% missing, was avg 8%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Jul 24, 2026 1:15pm
docs Ready Ready Preview, Comment Jul 24, 2026 1:15pm
eclipse Ready Ready Preview, Comment Jul 24, 2026 1:15pm
site Ready Ready Preview, Comment Jul 24, 2026 1:15pm

Request Review

@argos-ci

argos-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Jul 24, 2026, 1:24 PM

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6ad0a62f-82e0-4e14-bb28-b50507da1917

📥 Commits

Reviewing files that changed from the base of the PR and between 97c9b02 and 6293a4e.

📒 Files selected for processing (1)
  • apps/docs/scripts/lint-agent-ready.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/docs/scripts/lint-agent-ready.ts

Walkthrough

The change strengthens documentation HTML/Markdown parity checks, moves the hidden agent directive into the root layout, adds normalization for headings and details blocks, and introduces MCP proxy and rewrite routing with corresponding audit validation and documentation.

Changes

Agent-readiness documentation flow

Layer / File(s) Summary
Markdown parity normalization
apps/docs/src/lib/llm-markdown.ts, apps/docs/src/lib/get-llm-text.ts, apps/docs/src/components/api-page.tsx
Markdown processing restores heading markers, converts <details> blocks, preserves fenced-code content, and excludes the interactive API explorer from parity comparisons.
Root-level agent directive
apps/docs/src/app/layout.tsx, apps/docs/src/app/(docs)/(default)/[[...slug]]/page.tsx
The hidden llms.txt directive is rendered as the first body child by the root layout, and the docs page no longer renders the previous per-page agent block.
Audit validation and guidance
apps/docs/scripts/lint-agent-ready.ts, .claude/skills/docs-agent-ready/SKILL.md
Audit checks validate heading restoration, <details> conversion, directive placement, parity exclusions, and MCP wiring; skill guidance documents the updated invariants and reproduction steps.
MCP proxy and routing
apps/docs/src/app/mcp/route.ts, apps/site/next.config.mjs
The docs MCP route proxies supported methods and SSE traffic to the upstream server, while site rewrites route protocol requests based on request headers.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Site
  participant DocsMCP
  participant MCPServer
  Client->>Site: Send MCP request to /mcp
  Site->>MCPServer: Rewrite matching protocol traffic
  Client->>DocsMCP: Send request to /docs/mcp
  DocsMCP->>MCPServer: Proxy supported MCP method
  MCPServer-->>DocsMCP: Return streamed response
  DocsMCP-->>Client: Return response or redirect
Loading

Possibly related PRs

  • prisma/web#8087: Updates the same documentation markdown normalization and agent-facing content pipeline.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main changes: directive placement, markdown parity, and MCP endpoint work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent-score-directive-parity-mcp

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
.claude/skills/docs-agent-ready/SKILL.md (1)

65-73: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider pinning the afdocs CLI version in reproduction commands.

Static analysis flags the unpinned npx afdocs check ... invocations as a supply-chain "rug pull" risk (a compromised/updated upstream package could silently change behavior for anyone reproducing the audit).

-npx afdocs check https://www.prisma.io/docs --sampling deterministic -v
+npx afdocs@<pinned-version> check https://www.prisma.io/docs --sampling deterministic -v
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/docs-agent-ready/SKILL.md around lines 65 - 73, Pin the
afdocs CLI version in both reproduction commands under “Reproducing the audit”
by using an explicit version with npx, keeping the existing arguments and audit
behavior unchanged.

Source: Linters/SAST tools

apps/docs/scripts/lint-agent-ready.ts (1)

466-479: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

MCP site-rewrite check passes with only 1 of the 3 required header-matched rewrites.

mcpRewriteCount === 0 only fails if none of the /mcp rewrite blocks exist; per the routing comment in apps/site/next.config.mjs, all three header conditions (accept: text/event-stream, content-type: application/json, mcp-session-id) are needed to cover the full MCP Streamable HTTP transport (GET stream, POST messages, DELETE teardown). A regression that drops 2 of the 3 rewrites would still pass this check.

♻️ Proposed fix — require all three header-matched rewrites
-  const mcpRewriteCount = (
-    siteConfigSource.match(new RegExp(`source: "/mcp",\\s*\\n\\s*has:`, "g")) ?? []
-  ).length;
-  if (mcpRewriteCount === 0 || !siteConfigSource.includes(`destination: "${MCP_URL}"`)) {
+  const requiredMcpHeaderKeys = ["accept", "content-type", "mcp-session-id"];
+  const missingMcpHeaderRewrites = requiredMcpHeaderKeys.filter(
+    (key) => !new RegExp(`source: "/mcp",[\\s\\S]*?key: "${key}"`).test(siteConfigSource),
+  );
+  if (missingMcpHeaderRewrites.length > 0 || !siteConfigSource.includes(`destination: "${MCP_URL}"`)) {
     mcpEndpointErrors.push(
-      `site: next.config.mjs is missing the header-matched /mcp rewrites to "${MCP_URL}"`,
+      `site: next.config.mjs is missing /mcp rewrite(s) for header key(s): ${missingMcpHeaderRewrites.join(", ")}`,
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/docs/scripts/lint-agent-ready.ts` around lines 466 - 479, Update the MCP
rewrite validation in the site next.config.mjs check to require all three
header-matched /mcp rewrites, replacing the current zero-count threshold with a
count of three while preserving the destination URL validation and existing
error reporting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/docs/src/app/layout.tsx`:
- Around line 114-137: Update the hidden directive div in the layout’s body
content to include aria-hidden="true", and remove its embedded link from
keyboard navigation with tabIndex={-1}. Preserve the existing markup, text,
styling, and first-child placement so the raw HTML/DOM byte-position audit
remains unchanged.

In `@apps/docs/src/app/mcp/route.ts`:
- Around line 46-47: Bound request-body buffering in the route handler around
the request.arrayBuffer() call: reject requests whose declared Content-Length
exceeds the configured maximum, and read the body through a bounded reader that
stops and rejects once the limit is exceeded, including chunked requests without
a declared length. Preserve undefined bodies for GET and HEAD requests and only
invoke fetch after validation completes.
- Around line 49-54: Add an AbortController-based timeout around the upstream
fetch in the MCP route, applying the signal to fetch so stalled connection or
header acquisition is aborted after the configured duration. Preserve
upstream.body streaming behavior for SSE responses and ensure the controller
lifecycle does not prematurely cancel an established stream.

---

Nitpick comments:
In @.claude/skills/docs-agent-ready/SKILL.md:
- Around line 65-73: Pin the afdocs CLI version in both reproduction commands
under “Reproducing the audit” by using an explicit version with npx, keeping the
existing arguments and audit behavior unchanged.

In `@apps/docs/scripts/lint-agent-ready.ts`:
- Around line 466-479: Update the MCP rewrite validation in the site
next.config.mjs check to require all three header-matched /mcp rewrites,
replacing the current zero-count threshold with a count of three while
preserving the destination URL validation and existing error reporting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 232bb563-0a5c-4296-aacb-e289436e556b

📥 Commits

Reviewing files that changed from the base of the PR and between b13acad and d765846.

📒 Files selected for processing (9)
  • .claude/skills/docs-agent-ready/SKILL.md
  • apps/docs/scripts/lint-agent-ready.ts
  • apps/docs/src/app/(docs)/(default)/[[...slug]]/page.tsx
  • apps/docs/src/app/layout.tsx
  • apps/docs/src/app/mcp/route.ts
  • apps/docs/src/components/api-page.tsx
  • apps/docs/src/lib/get-llm-text.ts
  • apps/docs/src/lib/llm-markdown.ts
  • apps/site/next.config.mjs

Comment thread apps/docs/src/app/layout.tsx
Comment thread apps/docs/src/app/mcp/route.ts Outdated
Comment thread apps/docs/src/app/mcp/route.ts Outdated
- Hide the llms.txt directive from screen readers and the tab order
  (aria-hidden + tabIndex={-1}); audits read raw HTML, not the a11y tree.
- Bound the /docs/mcp proxy: reject request bodies over 1 MiB (413, also
  for chunked bodies without Content-Length) and abort the upstream fetch
  if headers take more than 30s, while keeping SSE bodies streaming.
- lint-agent-ready now requires all three header-matched /mcp rewrites
  (accept, content-type, mcp-session-id), not just one.
- Pin the afdocs version in the skill's reproduction commands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/docs/scripts/lint-agent-ready.ts`:
- Around line 473-475: The missingMcpHeaderRewrites validation currently matches
source and header keys across different rewrite entries. Update this check to
isolate each individual rewrite object, then validate that the same entry
contains source "/mcp", the required header key, and its destination together;
preserve the existing missing-key reporting behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 15900ced-5e2f-4091-99da-9695259cebb6

📥 Commits

Reviewing files that changed from the base of the PR and between d765846 and 97c9b02.

📒 Files selected for processing (4)
  • .claude/skills/docs-agent-ready/SKILL.md
  • apps/docs/scripts/lint-agent-ready.ts
  • apps/docs/src/app/layout.tsx
  • apps/docs/src/app/mcp/route.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/docs/src/app/layout.tsx
  • .claude/skills/docs-agent-ready/SKILL.md

Comment thread apps/docs/scripts/lint-agent-ready.ts
ankur-arch and others added 2 commits July 24, 2026 15:12
The per-key regex could pair `source: "/mcp"` from one rewrite with a
header key from a neighbouring entry (and ast-grep flagged the variable
RegExp). Split the config into per-rewrite chunks at each `source:` and
require the header key and MCP destination together in the same chunk,
using static string matching. Verified the guard now fails when an entry
is dropped or a destination is changed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ankur-arch

Copy link
Copy Markdown
Contributor Author

@coderabbitai resolve

All findings addressed:

  • aria-hidden + tabIndex={-1} on the hidden directive (97c9b02)
  • /docs/mcp proxy: 1 MiB body cap incl. chunked bodies, 30s header timeout with SSE streaming preserved (97c9b02)
  • afdocs version pinned in the skill (97c9b02)
  • MCP rewrite guard now scopes header key + destination to a single rewrite entry with static string matching, verified against dropped-entry and wrong-destination mutations (919a934)

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@ankur-arch Thanks for addressing all findings. I’m resolving the review comments. 🐇

✅ Action performed

Comments resolved and changes approved.

@ankur-arch
ankur-arch merged commit c98100a into main Jul 24, 2026
20 checks passed
@ankur-arch
ankur-arch deleted the agent-score-directive-parity-mcp branch July 24, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant