Skip to main content

Publish agent-readable content

Generate raw Markdown, llms.txt, and llms-full.txt from the same pages your site already publishes.

Expose a page collection once and let Ginko derive its agent-readable files. You do not need a second content tree or a script that copies Markdown into public/.

Expose a collection

Add site metadata, one section, and an agent policy to the collection:

content.config.ts
import {
  defineAgentSection,
  defineCollection,
  defineContentConfig
} from '@lupinum/ginko-content/config'

export const docs = defineCollection({
  type: 'page',
  source: 'docs/**/*.md',
  route: '/docs',
  agent: {
    section: 'docs',
    markdown: true
  }
})

export default defineContentConfig({
  agent: {
    site: {
      title: 'Acme documentation',
      description: 'Product documentation for Acme.',
      url: 'https://docs.example.com'
    },
    sections: [
      defineAgentSection({
        id: 'docs',
        title: 'Documentation',
        order: 10
      })
    ]
  },
  collections: { docs }
})

agent.markdown: true opts the collection into raw Markdown and both LLM indexes. The section ID must match an entry in agent.sections; use the built-in content section when you do not need custom grouping.

Set agent.site.url to the site's production origin. Ginko requires it when agent output is prerendered so generated links are absolute and unambiguous.

Know which config owns what

content.config.ts decides which pages and metadata appear in agent output. The content.agent module option in nuxt.config.ts controls how Nuxt serves that output.

Routes, link headers, Markdown negotiation, and prerendering are enabled by default when agent output exists. Configure only an option you want to change:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@lupinum/ginko-content'],
  content: {
    agent: {
      markdownNegotiation: false
    }
  }
})

Set content.agent: false to disable every agent route and middleware. This does not change the collection model in content.config.ts.

Use the generated surfaces

For a document served at /docs/getting-started, Ginko exposes:

  • /raw/docs/getting-started.md — normalized Markdown for that page.
  • /llms.txt — a compact, sectioned index with links to raw pages.
  • /llms-full.txt — the selected pages combined into one Markdown document.

Localized sites also get localized raw routes and /<locale>/llms.txt and /<locale>/llms-full.txt. Production agent output excludes drafts. Partials and data collections never become public agent pages.

On an SSR or hybrid deployment, an ordinary page can also return Markdown when a request sends Accept: text/markdown:

terminal
curl -H 'Accept: text/markdown' https://docs.example.com/docs/getting-started

That negotiation runs in Nitro middleware. A static host can serve an existing index.html without running Nitro, so static consumers must use /raw/**.md, /llms.txt, or /llms-full.txt instead.

Include an app-owned page

Use defineAgentAppPage() for a Nuxt route whose content does not come from a collection, such as a legal or support page. Add the helper to the existing import, then add a pages entry to the root agent object:

agent.pages
pages: [
  defineAgentAppPage({
    id: 'support',
    route: '/support',
    section: 'docs',
    title: 'Support',
    description: 'How to get help with Acme.',
    render: () => '# Support\n\nEmail support@example.com.'
  })
]

The app owns the HTML route; render() supplies only its agent-facing Markdown.

Serialize an MDC component

Ordinary Markdown needs no extra setup. Register a server-side serializer when a custom component needs a clearer text representation:

server/plugins/agent-markdown.ts
import {
  blockquoteMarkdown,
  getMarkdownProp,
  registerAgentMarkdownSerializers,
  type AgentMarkdownSerializer
} from '@lupinum/ginko-content/agent'
import { defineNitroPlugin } from 'nitropack/runtime'

const renderCallout: AgentMarkdownSerializer = (node, context) => {
  const title = getMarkdownProp(node, 'title') || 'Note'
  return blockquoteMarkdown(`**${title}**\n\n${context.renderChildren(node)}`)
}

export default defineNitroPlugin(() => {
  registerAgentMarkdownSerializers({
    callout: renderCallout,
    MdcCallout: renderCallout
  })
})

Register both the authored tag and its component alias when the parser can produce either name. Keep the serializer deterministic and free of private application state: its output is public content.

Verify static output

Generate the site, then inspect the public artifacts:

terminal
pnpm generate
find .output/public/raw -type f -name '*.md'

Open .output/public/llms.txt and .output/public/llms-full.txt as part of deployment review. Check that expected routes are present and that drafts, partials, and private data are absent.