Part I: an MCP client has no working directory, so it never sees your
AGENTS.md.
Part II: publish the server to the registry so a client can find
it. Now it can — and this part is about what belongs in the context it finds.
Horses for courses. A flat race wants the fastest horse. A course with fences
wants one that knows how to jump. Two different horses, and you would not swap
them.
The flat racer: Markdown, for instructions
AGENTS.md is a flat racer. Prose, fast to write, expressive, carries
judgment: use SerializerV2 for new features; V1 is back-compat only. Setup,
Build, Test, Conventions, Guardrails — how you work in this repo. The context
card renders exactly this: sections a client can navigate, the operational
manual an agent acts on.
For that course, prose is the right horse. Nothing beats a paragraph for "here
is the nuance, here is the exception."
The course has fences: the facts
Open the card as a human and the manual is not what you want first. First you
want the portrait: what is this, who is it for, why does it exist, where does it
run. The six W's. The card does not show them, because AGENTS.md sections
are a checklist, not a description.
You could add an ## About section. People do:
## About
mcp-context-card is a TypeScript MCP server. It runs on Node 22+, builds with
tsc, has no database (memory is a file), and deploys as a stdio process or a
stateless Streamable HTTP service. CI runs on GitHub Actions across three OSes.
Every clause is a fact that already lives somewhere machine-readable —
package.json, tsconfig.json, the Dockerfile, ci.yml. It has been copied
into prose, and prose is where it goes to rot.
I found one of these while writing this article. mcp-context-card's own
AGENTS.md said "Node 22 or newer." Release 1.0.1 had lowered the engines
floor to >=20 — package.json moved, the README followed, the AGENTS.md
prose did not. An agent reads "Node 22+" at full confidence and tells a
contributor on Node 20 they are unsupported. Wrong, and the file was sure.
That is a fence. So are these: the facts feed more than one reader (the agent,
the card, a catalog); you want to know which of the six W's you have not
answered; two people edit the repo and the prose diverges. A flat racer piles
into the first fence.
This is separation of concerns — the oldest idea in the trade. One place for
each kind of thing.
The structured layer defines. AGENTS.md instructs.
The jumper: schema, for definitions
Facts want structure. Here is project.faf — YAML, one file at the repo root:
project:
name: mcp-context-card
main_language: TypeScript
type: mcp
stack:
runtime: Node.js
build: TypeScript (tsc)
api_type: MCP (JSON-RPC 2.0 — stdio + stateless Streamable HTTP)
database: none # memory is a file
hosting: any Node host — stdio local, stateless HTTP remote
cicd: GitHub Actions — typecheck + build + test on 3 OSes
human_context:
who: MCP host and server implementers who want a project's context over MCP
what: the essential context / memory / identity components for MCP
why: AGENTS.md is the standard, but a client must know the file exists and read it whole
where: github.com/Wolfe-Jam/mcp-context-card
Diffable. One source. Scoreable — a completeness check tells you which W's are
still blank. And it is still YAML: a human reads it fine. You are not trading
readability for structure — you get both.
The example here uses .faf. It is IANA-registered
(application/vnd.faf+yaml), it ships with the scorer, and it is what
mcp-context-card reads — so it is the instance throughout.
— or use yours. The pattern is format-agnostic: a package.json plus a
short PROJECT.yaml, a custom schema, whatever your toolchain already emits.
The horse for this course is any structured source. The point is facts as
data, kept apart from the instructions.
Both horses, one card
The card only ever reads AGENTS.md — never project.faf, on purpose. That is
what keeps it vendor-neutral. So the facts reach it the same way any good
manual gets its spec sheet: you derive that part.
npx faf-cli export --agents # or the equivalent over MCP
human_context becomes a prose section at the top — the portrait. stack
becomes a facts block. Your hand-written conventions and guardrails stay exactly
where they were, outside the managed markers. Re-run it after a code change and
only the facts refresh.
Now the card shows the portrait and the manual, and neither drifts.
(Same "— or use yours": any tool that writes a facts section into AGENTS.md
from a structured source does this. agents-md-facts does the facts layer with
no project.faf at all.)
The loop
change the code
→ re-export facts section refreshes · project.faf re-scores
→ the card updates it just re-reads AGENTS.md
The manual stays true because you wrote it once and it rarely changes. The
portrait stays true because it is derived, not typed.
What this series was about
Prose for instructions, schema for facts — run the right one on each, and the
card shows both: the six W's a human reads first, the manual an agent acts on,
in one page, honest on their own.
Build the schema layer with .faf — or use yours. The point was never the
format. It was: a client should be able to see your context, and your
context should be worth seeing.
Series: Part I — Invisible AGENTS.md? · Part II — Publishing to the Registry · mcp-context-card · the .faf format.
Top comments (1)
@wolfejam, the split between structured project facts and hand-written operational judgment is a clean answer to AGENTS.md drift. Generating only the managed facts block preserves vendor neutrality while making runtime, CI, and deployment claims scoreable. Have you considered including provenance beside each generated field—source file plus key—so a reviewer or agent can trace a stale fact back to package.json or CI config without guessing?