Everything the atlas shows is derived from YAML under data/. This page says how to change
it. The rules come from the constitution and docs/data-model.md; the executable form is
src/schema/ and scripts/validate-data.ts.
pnpm validate:data # after every data edit; prints `path[index].field: message`
pnpm check:append-only # compares data/observations against origin/main
pnpm check # everything CI runs except the browser tests
pnpm build && pnpm test:e2e- Append the revision to
data/specs/mcp-authorization.yaml:id(the date as the spec names it),date,status: current,supersedes(the previous id),url,changes. - Set the previous revision's
statustosuperseded. Exactly one revision iscurrent. - For every feature that the new revision still defines, append an entry to its
revisionslist with the newrevision, thesectionheading as the document names it, theleveland, where the text qualifies it, alevelNote. A feature with no entry for the new revision is treated as removed in that revision. - Add new features (next section) for requirements the revision introduces.
pnpm validate:data, thenpnpm build: the changes page for the new pair of revisions and the matrix for the new revision are generated from the data. Observations against the old revision stay visible under/mcp/revisions/<old-id>; cells under the new revision start asunknownuntil someone observes them again.
- One file per feature in
data/features/<id>.yaml. Ids are dotted, lower case, and stable once published (mcp.<party>.<thing>):mcp.prm.*andmcp.rs.*for the MCP server,mcp.as.*for its authorization server,mcp.client.*for client-side requirements, which never appear in the matrix. revisionslists every revision that defines the feature, oldest first. The level is what the specification says about the party named inparty;levelNotecarries qualifiers such as "one of two" or "deprecated".observableispassivewhen published metadata shows it (the well-known documents, and theWWW-Authenticatechallenge, pointer and scope included, that the endpoint returns to a request without a token),activewhen a token, a scope or a protocol message is needed,manualwhen only documentation or source can show it. The atlas itself never sends requests (constitution III);observabletells the scanner what it may do.- A feature that replaces another sets
supersedes. Never delete a feature file that has observations; mark it removed by leaving it out of the newer revision.
The standards map at /standards/ is drawn from data/specs/*.yaml and
data/relations.yaml. Nothing is laid out by hand: a spec is placed one column right of what it
builds on, profiles or replaces, in the row of its publishing body.
- One file per spec in
data/specs/<id>.yaml. Ids are kebab-case and stable:rfc8693,oauth-2-1,cimd,eidas-2. Facts (title, dates, URLs) come from the publisher's own page: IETF Datatracker and the RFC Editor, openid.net, modelcontextprotocol.io, a2a-protocol.org, EUR-Lex, or the vendor's page for a vendor document. A spec whose facts cannot be confirmed there is not entered. bodyis who publishes it (otherfor CNCF, W3C and anyone the list does not name);familyis what kind of document it is (guidancefor best current practice and architecture documents that define no protocol).maturityis the publisher's status, not a grade:individual-draft,working-group-draft,candidate(implementer's draft, last call, submitted to the IESG),published(RFC, final specification, shipped revision),in-force(law),expired,withdrawn. Put the publisher's own label inmaturityNote("Proposed Standard", "Implementer's Draft 2", "Regulation (EU) 2024/1183").revisionslists dated revisions, oldest first, each with the exact document URL. Exactly one hasstatus: current(the one the map dates the spec by); earlier ones aresuperseded; a revision published but not yet the default isdraft. Usechangesfor what changed and what breaks, in the publisher's words where possible.- A withdrawn or expired document keeps its file; set
maturityand say why inmaturityNote. A withdrawn spec leaves the map and the export but keeps its page and relations. - Relations go in
data/relations.yaml, one entry per edge,from→to. Types:builds-on(cannot be used without the other),profiles(narrows the other for one setting),references(cites it for a mechanism),replaces(supersedes it),competes-with(a different answer to the same question) andsolves-same-problem-as(the same need by another route). The last two are judgement calls and need anotesaying what they are based on; addsourcewherever the document itself states the relation. The symmetric types are stored once per pair.builds-on,profilesandreplacesmust not form a cycle. pnpm validate:datarejects an unknown spec, a self relation, a duplicate, a missing note and a cycle, namingdata/relations.yaml[i].field.
- Create
data/scenarios/<id>.yaml(idequals the file name). Declare the actors in lane order, the features the chain exercises, the steps in order and the toggles. Every step and every toggle needscites: { spec, revision, section }with the document's own heading, and the revision must already exist indata/specs/. - A step that carries a token gets
token: { name, header?, claims, note? }with the claims as the specification's example would show them. Do not write change marks; the explorer derives them from the previous token in the chain. - A toggle maps a baseline step id to the steps that replace it (an empty list removes the
step). Replacement ids may reuse the replaced step's id and no other.
violateslists the feature ids the toggle defeats, so the page can point at the atlas. pnpm validate:data, then open/explorer/<id>/and each toggle page inpnpm dev. The e2e tests count diagram rows against list entries for every page.
- One file per server in
data/servers/<id>.yaml.listedsays why it is in the atlas andsourceis the public page that lists it (required unlesslisted: opt-in). accessandauthare the server's two classes (docs/data-model.md› Server): who can connect (public,closed,unknown) and how the endpoint authenticates clients (oauth,api-key,none,unknown). Each is{ class, method, evidence, observedAt, observedBy }with the vendor's documentation page as evidence;methodismanualwhen you read the page,vendor-claimwhen you only copied a directory's label. Correct a class in place with a newerobservedAtand the page that shows the change; git keeps the history. Leave the field out rather than guess: a missing class renders as a dashed "unknown" and the server is still scanned.auth: api-keyorauth: nonetakes the server out of the nightly scan and gives it "No OAuth" in its scan summary; when a scan's run summary says the endpoint's answer suggests another class, check the page and correct the file, not the observations.transport: stdio-onlymakes every cellnot-applicablewithout observations.- The seed list came from
jaw9c/awesome-remote-mcp-servers(research R4). To re-seed from a newer copy of that list, regenerate only servers that do not yet have a file; never overwrite files that carry observations.
Append to data/observations/<server-id>.yaml; never edit or remove an entry. The newest
observedAt wins; equal timestamps are broken by method strength (active-scan over
passive-scan over manual over vendor-claim). Use unknown to record that you looked and
could not tell, not-applicable when the feature cannot apply to that server.
The quickest way is the command, which validates the entry against the schema and the dataset before it appends anything:
pnpm observe figma mcp.as.dcr partial --by jbspeakr \
--evidence "https://… — registration admits allowlisted client names only; others get HTTP 403"
pnpm validate:data--method is manual by default, or vendor-claim; --revision defaults to the current one;
--at defaults to now. Then open a pull request. A manual entry stands against the nightly
scanner until the scanner sees something new for that cell (scanner.md › Running it); until
then each run summary lists the cell as "held by hand" when the scan disagrees.
The passive scanner (docs/scanner.md) writes observations with observedBy: scanner@<version>
and method: passive-scan. They follow the same append-only rule as everything else.
- Prefer
pnpm scan <server-id> --writeover a hand-writtenpassive-scanentry: the evidence string is then reproducible by anyone who runs the same command. - Reviewing a scanner pull request: open each evidence URL in the diff (every evidence starts
with the well-known URL it read) and check that the quoted field still says what the entry
claims.
unknownmeans the scanner could not read a document (timeout, 5xx, a redirect to another host, no JSON); it is not a verdict and never turns intounsupportedon the pages. - Bump
SCANNER_VERSIONinsrc/scanner/version.tswhenever a check's logic or evidence format changes. The next run then re-records every cell once, which is how the history shows which scanner produced which observation.
Set withdrawn: { at: <date>, reason: <text> } on the server. It leaves the matrix, the feature
pages, the sitemap and data.json; its page shows the notice; its observations stay in git.
- CI is green: schema and cross-reference validation, and the append-only check against the base branch.
- Open every
evidenceURL. It has to show the claimed behaviour for that server and feature under that revision. A note instead of a URL is acceptable only formanualobservations and must say what was done. observedAtis plausible andobservedByidentifies the contributor.- The status is the right one:
partialneeds a note inevidencesaying what is missing;unsupportedmust not be used for "could not tell" (that isunknown). - Merge. The deploy workflow publishes on
main.
Ids are public URLs and export keys. If a rename is unavoidable, add a redirect (see
docs/deploy.md), keep the old id in supersedes where the model allows it, and mention it in
the pull request description.
The Playwright screenshot tests compare against baselines in tests/e2e/__screenshots__.
Baselines are only valid for the Chromium build Playwright pins, because another build
rasterises fonts a few percent differently. After an intended visual change, either run
pnpm exec playwright install chromium && pnpm build && pnpm test:e2e --update-snapshots
locally with that pinned build, or add the refresh-screenshots label to the pull request:
the screenshots workflow regenerates the baselines with CI's browser, commits them to the
branch and removes the label. A push made by that workflow does not start CI, so push any
further commit (or merge the base branch) to get a green run.
Scanner pull requests must not need new baselines. The scanner screenshots set every
[data-nightly] element (counts and dates the nightly run changes) to a fixed value and mask the
counts and report rows, so mark any new nightly-changing text on those pages with data-nightly.