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
.mdaliases andAccept: text/markdownnegotiation -
default and localized page content
-
actionable missing-page recovery
-
llms.txtandllms-full.txt -
AGENTS.mdandskill.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
versionand an incorrectallowed-toolstype are reported as conformance failures -
cursor-based structured search pagination (see below)
-
capabilities.contentChangesflag and a liveGET /api/docs?audience=agent&response=changesendpoint returning a validdocs-content-changes.v1JSON payload withindexGeneration,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.
Cursor-based structured search
All first-party adapters support cursor pagination for structured search. The conformance suite verifies that:
GET /api/docs?response=structured&query=<term>&limit=1returnshasMore,total, andnextCursorin the response body.- Supplying the returned
nextCursoras?cursor=<token>fetches the next page. - Legacy array responses (without
?response=structured) remain unchanged — nocursor,nextCursor,hasMore, ortotalfields are injected. - Passing an invalid or stale
cursorwithout?response=structuredreturns HTTP400.
The discovery spec (/.well-known/agent.json) advertises four pagination const fields under
search:
| Field | Value |
|---|---|
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
GETresponse for a machine-readable route (/docs.md,/llms.txt,/.well-known/agent.json, etc.) includes:- A strong
ETagmatching 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 strong
- A
HEADrequest returns the sameETagandContent-Digestheaders with an empty body. - A conditional
GETwithIf-None-Matchequal to the returned ETag receives304 Not Modifiedwith theContent-Digestheader preserved and an empty body. 304responses 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
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, andallowed-tools - Set
allowed-toolsas a space-separated string — not a YAML list - Keep
namewithin 64 characters,descriptionwithin 1024 characters, andcompatibilitywithin 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:
pnpm --filter @farming-labs/docs test adapter-agent-conformance.test.ts
pnpm --filter @farming-labs/svelte typecheckValidate that a framework adapter exposes the same agent-readable documentation contract as the first-party adapters
How is this guide?