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 4 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.
There was a problem hiding this comment.
Valid, and fixed in f1a9884 — though the two halves of this had different statuses.
PDF rendering was already handled in 14a33fb, pushed ~2 minutes after the commit this review ran against (1b30c45). app/api/md-to-pdf/route.ts strips everything from AGENT_FOOTER_MARKER onwards before parsing.
The .md route was still affected, which the PDF fix did not cover: https://langfuse.com/privacy.md and /cookie-policy.md still ended with "Found an error in these docs? open an issue", appended to a legal document. Taking the other suggested route for this — privacy.md, cookie-policy.md, and imprint.md now skip the footer entirely via AGENT_FOOTER_EXCLUDED_PAGES. The PDF strip stays in as a second line of defense for any page that gains a md-to-pdf link later.
I used a narrow exclusion list rather than an allowlist of docs/integrations/self-hosting, because blog/, changelog/, guides/, faq/, and content/security/** (the trust center — "is Langfuse SOC 2 compliant?") are all pages agents legitimately land on and should be able to navigate out of. changelog/ already carries its own agent notice, so it clearly belongs to the agent surface.
Added verifyFooterExclusions(), which fails the build if an entry in the set no longer matches a generated page — otherwise renaming a legal page silently turns its exclusion into a no-op and the footer creeps back. Verified it exits 1 with a clear message on a stale entry.
Result: 950 of 953 pages carry the footer; the 3 legal pages do not.
PR review flagged that the footer was appended to every generated page, including privacy.md, cookie-policy.md, and imprint.md. A privacy policy whose last section is curl examples, MCP connection details, and "found an error? open a GitHub issue" reads as a copy-paste error — and privacy and cookie-policy are the two pages that expose a "download as PDF" link. The PDF export already strips the footer (14a33fb), which covered the PDF half of the report, but the .md route still served it on /privacy.md and /cookie-policy.md. These pages now skip the footer entirely; the PDF strip stays as a second line of defense for any page that gains a PDF link later. verifyFooterExclusions() fails the build if an entry in AGENT_FOOTER_EXCLUDED_PAGES no longer matches a generated page, so renaming a legal page cannot silently turn its exclusion into a no-op. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The docs MCP server is already documented in llms.txt and on /docs/docs-mcp, and an agent reading a page through getLangfuseDocsPage does not need the page to tell it about MCP. The region line was general SDK guidance rather than something the footer needs to carry on every page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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:
Note
Low Risk
Build-time markdown generation and PDF preprocessing only; no auth or runtime API behavior changes beyond stripping a known HTML comment marker in PDF generation.
Overview
Adds a shared Agent Instructions footer to generated
public/md-srcMarkdown so agents landing on a single page can discover.mdaccess,/api/search-docs,llms.txt, the agent skill, and related guidance without guessing.Build pipeline: New
lib/agent-instructions-footer.jsis appended inscripts/copy_md_sources.jsfor all generated pages andmd-override/copies, with privacy, cookie-policy, and imprint excluded and a post-build check that those paths still exist.llms.txtgains Markdown Access and Docs Search API sections (before MCP).docs-mcp.mdxgets a#rest-endpointanchor for deep links.PDF export:
/api/md-to-pdfstrips content fromAGENT_FOOTER_MARKERonward so human PDFs do not include agent-only curl/MCP blocks.Reviewed by Cursor Bugbot for commit 91891b7. Bugbot is set up for automated code reviews on this repo. Configure here.