Home /

docs

Adapter Agent Conformance

Every @farming-labs/docs adapter should expose the same machine-readable surface. The shared conformance runner turns that expectation into an executable contract instead of relying on each adapter to maintain a separate checklist.

Covered surfaces

The versioned contract (DOCS_AGENT_CONTRACT_VERSION 1.3) verifies:

  • custom agent discovery, RFC 9727 API catalog GET/HEAD, Agent Skills index/artifact GET/HEAD, config, diagnostics, and feedback schema

  • explicit .md aliases and Accept: text/markdown negotiation

  • default and localized page content

  • actionable missing-page recovery

  • llms.txt and llms-full.txt

  • AGENTS.md and skill.md

  • XML and Markdown sitemaps plus robots.txt

  • a Streamable HTTP MCP initialize request

  • gzip-encoded tar archive delivery of SKILL.md (decompressed and validated by readAgentSkillDocumentFromTar)

  • SKILL.md frontmatter validated against the full spec — unexpected fields such as version and an incorrect allowed-tools type are reported as conformance failures

  • cursor-based structured search pagination (see below)

  • capabilities.contentChanges flag and a live GET /api/docs?audience=agent&response=changes endpoint returning a valid docs-content-changes.v1 JSON payload with indexGeneration, mode, resetRequired, documentCount, and array fields; ETag and body-free delivery are also verified

  • HTTP cache validators on every agent-surface response (see below)

The first-party Next.js, TanStack Start, SvelteKit, Astro, and Nuxt adapters run this same contract in their package tests.

All first-party adapters support cursor pagination for structured search. The conformance suite verifies that:

  • GET /api/docs?response=structured&query=<term>&limit=1 returns hasMore, total, and nextCursor in the response body.
  • Supplying the returned nextCursor as ?cursor=<token> fetches the next page.
  • Legacy array responses (without ?response=structured) remain unchanged — no cursor, nextCursor, hasMore, or total fields are injected.
  • Passing an invalid or stale cursor without ?response=structured returns HTTP 400.

The discovery spec (/.well-known/agent.json) advertises four pagination const fields under search:

FieldValue
cursorParam"cursor"
nextCursorField"nextCursor"
hasMoreField"hasMore"
totalField"total"

These fields are absent from the legacy v1 schema and present in agent manifest v2. An adapter that omits them from its discovery output will fail the analyzeAgentSurfaceDrift check.

HTTP cache validators

All first-party adapters now attach strong HTTP cache validators to every agent-surface response. The conformance suite verifies that:

  • Every GET response for a machine-readable route (/docs.md, /llms.txt, /.well-known/agent.json, etc.) includes:
    • A strong ETag matching the pattern "<64-hex-char sha256>" — derived from the exact response bytes.
    • A Content-Digest: sha-256=:…: header (RFC 9530) whose base64 value matches the same bytes.
  • A HEAD request returns the same ETag and Content-Digest headers with an empty body.
  • A conditional GET with If-None-Match equal to the returned ETag receives 304 Not Modified with the Content-Digest header preserved and an empty body.
  • 304 responses never include a body.

Adapters that build responses through createDocsCacheableResponse satisfy these requirements automatically. Custom adapters must produce the same header shape and honour conditional-request semantics.

Use it in an adapter test

src/agent-conformance.test.ts
import { expect, it } from "vitest";
import { runDocsAgentConformance } from "@farming-labs/docs";
import { createDocsServer } from "./server";

it("implements the agent surface contract", async () => {
  const server = createDocsServer({
    entry: "docs",
    i18n: { locales: ["en", "fr"], defaultLocale: "en" },
    mcp: true,
    sitemap: true,
    robots: true,
    _preloadedContent: {
      "/docs/en/page.md": "---\ntitle: Introduction\n---\n# Introduction",
      "/docs/fr/page.md": "---\ntitle: Introduction\n---\n# Introduction\n\nBonjour",
    },
  });

  const report = await runDocsAgentConformance({
    adapter: "sveltekit",
    async handle(request, surface) {
      if (surface === "mcp") {
        return server.MCP.POST({ request });
      }

      return server.GET({ request, url: new URL(request.url) });
    },
  });

  expect(report.cases.filter((result) => !result.passed)).toEqual([]);
});

The report includes the contract version and an individual result for every surface. Failure messages include the unexpected status, content type, or missing response text so adapter drift is visible in CI.

SKILL.md frontmatter in the contract

As of contract version 1.3, the conformance runner fetches each skill artifact, decompresses the gzip-encoded tar archive, and validates the embedded SKILL.md frontmatter against the strict spec.

A SKILL.md that passes the contract must:

  • Contain only the standard fields: name, description, license, compatibility, metadata, and allowed-tools
  • Set allowed-tools as a space-separated string — not a YAML list
  • Keep name within 64 characters, description within 1024 characters, and compatibility within 500 characters

Any other top-level key (e.g. version) causes DocsAgentSkillFrontmatterError to be thrown and the conformance case to fail. Skills with non-standard frontmatter are not published to the standards discovery index.

Publishing a skill from source

When building a published skill programmatically, buildDocsPublishedAgentSkill now accepts directoryName and source options used for frontmatter name-matching and error reporting:

import { buildDocsPublishedAgentSkill } from "@farming-labs/docs";

const skill = buildDocsPublishedAgentSkill(skillDocument, hash, {
  directoryName: "my-tool",    // must match the `name` field in frontmatter
  source: "/path/to/SKILL.md", // included in error messages
});

Extending the contract

Add a new invariant to createDocsAgentContractCases once, then update every first-party adapter in the same change. Keep backwards-compatible transitions explicit; for example, the missing-page case temporarily accepts both the legacy recovery status and a standards-correct 404, while requiring the useful recovery document in either response.

Run the relevant adapter test and core typecheck before publishing:

terminal
pnpm --filter @farming-labs/docs test adapter-agent-conformance.test.ts
pnpm --filter @farming-labs/svelte typecheck
Customize
Live preview on these docs
Reset
Customize
Export Code
Preset
Explore more themes
Primary Color
Primary
Colors
Background
Foreground
Muted Text
Border
Card
Muted BG
Ring
Layout
SidebarBordered
TOC StyleDirectional (tree)
TOC DepthH2 + H3
Ask AI
ModeFloating
Floating StyleFull Modal
PositionBottom Right
Features