docs: add agent instructions footer to .md pages, document markdown + search API in llms.txt - #3426
docs: add agent instructions footer to .md pages, document markdown + search API in llms.txt#3426jannikmaierhoefer wants to merge 2 commits into
Conversation
…n/search in llms.txt Agents reach our markdown through the ".md" URL suffix, Accept: text/markdown negotiation, or the docs MCP getLangfuseDocsPage tool, but a page gives them no way to discover the rest of our agent surface. An agent that cannot answer from the current page has to guess. Every generated page in public/md-src now ends with a short "Agent Instructions" section (the pattern GitBook-hosted docs use) naming: the .md convention, /api/search-docs for semantic search, llms.txt, the docs MCP server, the Langfuse agent skill, the CLI, the API reference, and a warning not to hardcode the EU region. Applied to md-override/ pages too. llms.txt previously documented the MCP server and skill but not the .md convention or /api/search-docs; both now have their own sections with runnable curl examples. The footer heading is an H2 because scripts/check-h1-headings.js walks public/md-src after a local build and would fail on a second H1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@claude review |
/api/md-to-pdf renders from public/md-src, so the agent instructions footer would have appeared in every human-facing PDF download. Drop everything from the marker onwards before parsing. The marker constant is exported from lib/agent-instructions-footer.js so the two stay in sync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| /** | ||
| * Append the agent instructions footer to a page's markdown. Applied to every | ||
| * generated page (including md-override/ files) so agents that land on any | ||
| * single .md page can discover the rest of the agent surface: the ".md" | ||
| * convention, /api/search-docs, llms.txt, the docs MCP server, and the skill. | ||
| * @param {string} markdown | ||
| * @param {string} destRel path relative to public/md-src, e.g. "docs/foo.md" | ||
| * @returns {string} | ||
| */ | ||
| function appendAgentInstructionsFooter(markdown, destRel) { | ||
| const footer = buildAgentInstructionsFooter(`${SITE_URL}/${destRel}`); | ||
| return `${markdown.replace(/\s*$/, "")}\n\n${footer}`; |
There was a problem hiding this comment.
🔴 appendAgentInstructionsFooter() in scripts/copy_md_sources.js runs unconditionally on every generated page, so it also gets appended to public/md-src/privacy.md and cookie-policy.md, and app/api/md-to-pdf/route.ts renders that markdown straight to PDF without stripping the footer. That means the 'download as PDF' link on the Privacy Policy and Cookie Policy pages now ships end users a legal document that ends with a curl/MCP/GitHub-issue 'Agent Instructions' section. Fix by excluding marketing/legal pages from the footer in copy_md_sources.js, or stripping the AGENT_FOOTER_MARKER block in md-to-pdf/route.ts before rendering.
Extended reasoning...
This PR adds appendAgentInstructionsFooter() and calls it unconditionally inside copyAll() (scripts/copy_md_sources.js:198) for every file written to public/md-src/**, with no exclusion by content type or section. lib/content-dir-map.js maps the marketing content directory to the empty URL prefix (marketing: ""), so content/marketing/privacy.mdx and cookie-policy.mdx are written out as public/md-src/privacy.md and public/md-src/cookie-policy.md — both now carrying the new ## Agent Instructions footer (curl examples for /api/search-docs, MCP server endpoint info, agent-skill install instructions, and a "Found an error in these docs? open an issue on GitHub" line).
Both privacy.mdx and cookie-policy.mdx contain a human-facing "download as PDF" link (<a href="/api/md-to-pdf?url=/privacy.md">, similarly for cookie-policy). app/api/md-to-pdf/route.ts rewrites that URL to /md-src/<path>.md, fetches the file verbatim, strips only the YAML frontmatter block, runs stripMdxForPlainMarkdown and removeAnchorTags, and renders the remainder to HTML/PDF via marked. None of those steps recognize or strip the AGENT_FOOTER_MARKER/## Agent Instructions block, so it renders straight into the generated PDF.
Proof, step by step:
scripts/copy_md_sources.jscopiescontent/marketing/privacy.mdx→public/md-src/privacy.md, then unconditionally callsappendAgentInstructionsFooter(processed, "privacy.md"), appending the full footer block after a---separator.- A site visitor on
/privacyclicks "download as PDF", which hits/api/md-to-pdf?url=/privacy.md. - The route fetches
public/md-src/privacy.mdverbatim, strips frontmatter only, and converts the rest (including the appended footer) to PDF. - The resulting PDF — the one a user or a legal/compliance reviewer downloads as "the Privacy Policy" — ends with a section containing
curl -sG "https://langfuse.com/api/search-docs" ..., MCP server connection details, a skill-install instruction, and a GitHub issue link.
Nothing crashes and the PDF still generates correctly, but the content is objectively wrong for the document type: a Privacy Policy / Cookie Policy is a legal artifact, and shipping AI-agent tooling instructions inside it is inappropriate and would look like a bug (or a copy-paste error) to any reader who scrolls to the end, including legal/compliance reviewers relying on that PDF as the canonical policy text. This is a fresh regression introduced by this PR — before it, public/md-src/privacy.md (and the PDF derived from it) was clean policy text.
The fix is small: either skip the footer for non-agent-relevant sections (e.g., only apply it under docs/integrations/self-hosting, matching the PR's own stated intent of making "the docs" self-describing for agents — the Privacy Policy is not part of that surface), or strip the AGENT_FOOTER_MARKER-delimited block in app/api/md-to-pdf/route.ts before rendering to PDF, alongside the existing frontmatter-stripping step.
Motivation
Agents reach our Markdown through three paths — the
.mdURL suffix,Accept: text/markdownnegotiation, and the docs MCPgetLangfuseDocsPagetool — but a page currently tells them nothing about the rest of our agent surface. An agent that lands on a page which doesn't answer its question has to guess.Compare
docs.reo.dev(GitBook), where every.mdresponse ends with an# Agent Instructionsblock documenting a?ask=RAG endpoint. That's how an agent self-discovers the capability without being told. We have the equivalent capabilities —/api/search-docs,/api/mcp,llms.txt, the agent skill, the CLI — but nothing advertises them at the point of use.Changes
1. Agent instructions footer on every
.mdpage — newlib/agent-instructions-footer.js, appended inscripts/copy_md_sources.jsto all 953 generated pages plus the 2md-override/pages. It covers:.md/Accept: text/markdownconvention, and this page's own Markdown URL/api/search-docswith a runnablecurlexamplellms.txtand the per-section indexesapi.reference.langfuse.comrather than inferring from examplescloud.langfuse.com— read the host fromLANGFUSE_BASE_URL2.
llms.txtgains## Markdown Accessand## Docs Search APIsections — it already documented the MCP server and skill, but neither the.mdconvention nor/api/search-docs.3.
## REST Endpoint [#rest-endpoint]anchor added tocontent/docs/docs-mcp.mdxso the newllms.txtdeep link resolves.Notes
scripts/check-h1-headings.jswalks the whole repo includingpublic/md-srcafter a local build, and a second#heading would fail thecheck_h1job.Verification
node scripts/copy_md_sources.js→ 953 files + 2 overrides, each with exactly one footernode scripts/check-h1-headings.js→ passes across generated outputprettier --check→ clean on all four filesllms.txtblock rendered and inspected (the script itself needspublic/sitemap-0.xmlfrom a full build)🤖 Generated with Claude Code
Follow-up in this PR: PDF export fix
/api/md-to-pdfrenders frompublic/md-src, so the footer would have shipped inside every human-facing PDF download.app/api/md-to-pdf/route.tsnow strips everything fromAGENT_FOOTER_MARKERonwards before parsing. Verified: 30,880 → 28,544 bytes ondocs/observability/get-started.md, original ending restored, and markdown without the marker passes through untouched.The PDF route is the only human-facing
md-srcconsumer — the other paths (.mdrewrite,Accept: text/markdownnegotiation, MCPgetLangfuseDocsPage) are all agent-facing and should keep the footer.Greptile Summary
Adds agent-discovery guidance to generated Markdown documentation.
llms.txt.Confidence Score: 4/5
The PR appears safe to merge, with a non-blocking documentation correction needed for the homepage Markdown URL convention.
The generated footer works for ordinary and overridden page paths, but its universal suffix instruction directs homepage consumers to
/.mdeven though the generated root artifact is/md-src/index.md.Files Needing Attention: lib/agent-instructions-footer.js
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "docs: add agent instructions footer to ...." | Re-trigger Greptile
Context used: