A coding agent will use your Button. Then it hits a state your system doesn't document and invents a spacing value, a colour, a variant that looks plausible and belongs to nothing. The Agent-Ready Starter is a reference architecture for the half your docs site can't serve: 8 components in a shadcn-spec registry, 139 DTCG tokens, and a working MCP server that make your system answerable.
All five signals
Foundations for MCP, llms.txt, DTCG, registry and Code Connect. Registry hosting and the Figma mappings require configuration.
Designsystems.one · Registry
The machine-readable half of a design system, as code you own.
Counted from the kit
8 components, 139 DTCG tokens, 3 MCP tools, 0 dependencies in the build.
or write to kir.zhukovsky@gmail.com — subject prefilled
Zip · 8 components · 139 tokens · MCP server · instant download · one-time purchase
This site's own registry index, in the shape the kit's registry.json uses — no email, no account.
Kiryl Zhukouskimaintainer of the Agent-Ready Index — 37 systems, audited quarterly.
Agent-Ready Starter
or write to kir.zhukovsky@gmail.com — subject prefilled
01 · What you get
8 components — button through switch
Button, input, field, card, badge, alert, dialog and switch as shadcn-spec registry items: TSX with forwardRef, cva variants, and a per-item dependency list so Badge doesn't drag in Radix Dialog.
139 tokens — W3C DTCG, three tiers
$value, $type and a hand-written $description on every leaf, {dot.path} aliases between the tiers, and 28 dark values carried in $extensions.
3 tools — a stdio MCP server that runs
list_components, get_component and get_tokens over JSON-RPC 2.0. Ask for a component the registry doesn't have and the error names all 10 items it does.
2 templates — llms.txt and DESIGN.md
Filled in, with a TODO marker at every decision that is yours — including the two sections nobody writes: what your system leaves uncovered, and what is deprecated.
2 mappings — Figma Code Connect
button.figma.tsx is the straightforward case. field.figma.tsx is the one worth reading: one Figma component with toggles, one React wrapper composing an Input, absorbed in the mapping.
0 dependencies — the build gates itself on contrast
Around 250 lines of Node turn the DTCG file into tokens.css, score 9 text pairs against WCAG 2.1 AA in both themes, and exit non-zero below 4.5:1.
02 · Look inside
Ask the kit's MCP server for a component that doesn't exist and the refusal is legible: the error names every component that does exist, then tells the model not to invent one. That message is the mechanism — it turns “add a toast” from an improvisation into a decision someone gets to make.
No registry item named 'toast'.
Available: cn, tokens, button, input, field, card, badge, alert, dialog, switch.
Do not create a new component — pick one of these or ask the user.Verbatim output. The error is written for the model, not for a log file: it names all 10 registry items and closes the door on an eleventh.
The prompt
Add a destructive Delete button that matches our design system.
Without a registry
With this kit wired in
And what it still doesn't do
03 · Free sample
This site serves its own registry at /r, answers a public MCP endpoint at /api/mcp, and publishes the Index the five signals are scored against. All three are live on this domain, open to a browser or an agent, and cost nothing. The $99 packages that stack as files in your own repo.
Browse the live registryJSON · no email · nothing to install
04 · Fit
The 8 components are reference implementations of the pattern — registry entry, token discipline, MCP exposure — meant to be copied, restyled, and owned. What the kit wires is the machine-readable half of the system you already have.
You have the components and none of the wiring
Your Button ships. The MCP server, the DTCG file and the registry JSON do not exist yet — this kit is that half, as ten files.
You want to own the code
Ten files land in your repo and stay yours from that minute. No npm package to depend on, no version to pin, nobody patching your Button for you.
Tokens alone would earn the hour
tokens.css is 139 plain custom properties. Copy it in, import it in the root layout, and the framework question never comes up.
Your agents keep inventing values
Spacing numbers and variant names that look plausible and belong to nothing. get_tokens and get_component turn both of those into a lookup.
Skip it if
Score your own system first on the Agent-Ready Index — 37 public systems, audited 2026-06-10.
05 · Price
The kit is built and it runs; the checkout isn't wired yet. The button below opens an email. Two emails at most: one when a launch date exists, one when it ships. No card required.
Agent-Ready Starter · Being packaged
$99One-time purchase, instant download, 8 components and 139 tokens you own from the minute they land.
or write to kir.zhukovsky@gmail.com — subject prefilled
The boring terms
This kit wires the machine-readable half; the Migrate Playbook sequences the componentization work that comes before it.
06 · Details
Every row below is a path that exists, with a claim you can check by running it.
registry.json + registry/10 shadcn-spec registry itemsEight reference components — button, input, field, card, badge, alert, dialog, switch — plus the cn() helper and the token theme. TSX with forwardRef, cva variants, and per-item dependency lists so someone who only wants Badge doesn't get Radix Dialog.
tokens/tokens.dtcg.json139 tokens in W3C DTCG format$value / $type / $description on every leaf, across three tiers, with {dot.path} aliases and dark-mode values carried in $extensions. Every description is written by hand; no generator produced this file.
tokens/build.mjsDTCG → CSS in ~250 lines, 0 dependenciesResolves aliases with cycle detection, refuses two tokens under one CSS name, emits the dark block from the token file, and gates the build on contrast. Also runs --check in CI to catch a stale committed tokens.css.
mcp/server.mjsWorking stdio MCP server, 3 toolslist_components, get_component, get_tokens over JSON-RPC 2.0 — a hand-written stdio transport — plus clean empty resources/prompts lists for strict clients.
smoke-test.mjsThree checks, one commandRuns the token build and counts the custom properties in the output, performs the MCP handshake over stdio and asserts the three tools, and verifies every file registry.json lists exists. One ok line per check, non-zero exit on the first failure.
templates/llms.txtFilled-in llms.txt templateCanonical URLs, the rules an agent must not break, the component list, and sections for 'Not in this system' and 'Deprecated — do not use'. TODO markers mark every place a decision is yours.
templates/DESIGN.mdThe judgement calls a token can't holdToken architecture, layout and density, the eight component decisions agents reliably get wrong, the five states every data view needs, the accessibility floor, and voice. Written out, with TODOs where your team differs.
code-connect/Two annotated Figma mappingsbutton.figma.tsx is the straightforward case. field.figma.tsx is the one worth reading: Figma models a form field as one component with toggles, React models it as a wrapper composing an Input — the comments show how to absorb that in the mapping instead of adding a redundant prop.
COMPATIBILITY.mdThe support matrix, floors namedNode ≥ 18 and the file that sets each floor. React 18 and 19, with the three client components listed. Tailwind v3.3+ or v4 for the components, no framework at all for the tokens. The registryDependencies rewrite the shadcn CLI needs before self-hosting. What the MCP server answers, and integration-time ranges from an hour to two days.
README.mdInstall steps and a scope sectionIncluding what the kit is not: no npm package, no hosted registry, no select or table or date picker, and Code Connect files that cannot be real until they point at your Figma node.
Not in the box
A flat token file hands a model every value as an equally plausible choice. A tiered one narrows the legal set and names each option by intent, so “the background of a primary button” resolves to exactly one token instead of a plausible-looking wrong one. Rebranding becomes one edit to the ramp plus a rebuild — and if a rebrand needs a component file touched, a token is missing.
primitive.color.brand.600 = #4f46e5Raw values with no meaning attached. A component may never reference this tier — that rule is the whole architecture.
semantic.color.action.solidBg → {brand.600}28 colour roles that describe intent, not appearance. This is the vocabulary components are allowed to use, and the only tier that flips in dark mode.
component.button.height.md = 40pxPer-component decisions. button.height.md and input.height are equal on purpose, and the token file says why in its $description.
Indentation is the rule, not decoration: a tier may only point downward. The build fails on an unknown alias and on a cycle.
70 colour tokens exist in the set. Only the 28 semantic roles are legal inside a component — the primitives exist for the semantic tier to point at. Counted from tokens.dtcg.json.
node tokens/build.mjs turns the DTCG file into tokens.css, scores 9 text pairs against WCAG 2.1 AA in both themes, and exits non-zero below 4.5:1 — a floor wired into the build rather than an audit someone remembers to run. Zero dependencies: no Style Dictionary, no config file for an agent to guess at.
The gate that caught us
$description records why, and the worst passing pair today is 4.52:1. Fix the ramp, not the threshold.These are lifted from packages/agent-ready-starter as it stands. Where a file was too long for this column it is cut with a … line — those lines are truncation markers added here, not content in the file — and a few JSON entries are line-wrapped. Nothing else is changed.
tokens/tokens.dtcg.jsonjson{
"primitive": {
"color": {
"brand": {
"600": { "$value": "#4f46e5", "$type": "color",
"$description": "Solid action background (light mode)." }
… 41 more primitive colour steps …
}
}
},
"semantic": {
"color": {
"action": {
"solidBg": { "$value": "{primitive.color.brand.600}", "$type": "color",
"$description": "Primary button background.",
"$extensions": {
"one.designsystems.dark": "{primitive.color.brand.500}" } },
"solidFg": { "$value": "{primitive.color.neutral.0}", "$type": "color",
"$description": "Primary button label.",
"$extensions": {
"one.designsystems.dark": "{primitive.color.neutral.950}" } }
… subtleBg, subtleFg, solidBgHover …
}
}
},
"component": {
"button": {
"height": {
"md": { "$value": "40px", "$type": "dimension",
"$description": "Default button height. Matches input height so
they line up in a row." }
}
}
}
}All three tiers in one file. A primitive holds a hex and no meaning; the semantic role points at it with a {dot.path} alias and carries its dark value in $extensions, which is where the 28dark overrides come from; the component tier records the decisions a role can't justify — including why button and input heights are equal.
registry/ui/badge.tsxtsxconst badgeVariants = cva(
cn(
"inline-flex items-center gap-[var(--ds-space-1)]",
"px-[var(--ds-badge-padding-x)] py-[var(--ds-badge-padding-y)]",
"rounded-[var(--ds-badge-radius)] border",
"text-[length:var(--ds-text-help)] font-medium leading-none whitespace-nowrap",
),
{
variants: {
tone: {
neutral:
"border-[var(--ds-color-border-default)] bg-[var(--ds-color-bg-subtle)] …",
brand:
"border-transparent bg-[var(--ds-color-action-subtle-bg)] …",
danger:
"border-[var(--ds-color-danger-border)] bg-[var(--ds-color-danger-bg)] …",
… success, warning, solid …
},
},
defaultVariants: { tone: "neutral" },
},
)
export function Badge({ className, tone, ...props }: BadgeProps) {
return <span className={cn(badgeVariants({ tone }), className)} {...props} />
}One of the 8 reference components, minus its imports and type declarations, with three of the six tones cut for width. Note what is absent from what remains: no hex, no spacing number, no font size. Every visual decision is a var(--ds-*) lookup, which is the property that lets an agent restyle the kit by editing the token file instead of editing eight components and missing two.
mcp/server.mjs — tools/listjson-rpc$ printf '%s\n' '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| node mcp/server.mjs
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "list_components",
"description": "List every component in this design system's registry:
name, type, title, description, npm dependencies, registry
dependencies, and the path each file installs to. Call this FIRST —
before writing any UI — so you use the components that exist instead
of inventing new ones.",
"inputSchema": {
"type": "object",
"properties": {
"type": { "type": "string", "description": "Optional filter on
registry type, e.g. 'registry:ui' …" }
}
}
},
{ "name": "get_component", "inputSchema": { …, "required": ["name"] } },
{ "name": "get_tokens", "inputSchema": { … "format": {
"enum": ["dtcg", "css", "flat"] } } }
]
}
}The response to the command above it, unedited. 3 tools over JSON-RPC 2.0. The kit ships with zero dependencies, and rather than make @modelcontextprotocol/sdk its first one, the stdio loop is written by hand — initialize, ping, tools/list, tools/call. The tool descriptions are written to be read by a model, which is why list_componentssays “call this FIRST” in its own text.
README.md — installbash# Node >= 18. Nothing to install first — the build and the
# server have no dependencies.
cd packages/agent-ready-starter
# Writes tokens.css and prints the contrast table. Exits
# non-zero if a text pair drops below 4.5:1.
node tokens/build.mjs
# Components: once you serve registry.json from your own
# domain, the standard shadcn CLI installs them.
npx shadcn@latest add https://your-domain.com/r/button.jsonOne catch: until you serve registry.json from your own domain there is no URL for the CLI to fetch, so the first install is copying ten files to the targets listed in the registry manifest. The README says that in the same words, in the same section.
Four steps, in order, each one a command you can run. Node 18 or newer is the only prerequisite — the token build and the MCP server have no dependencies at all, so there is nothing to install before step one. Once the kit lands in your repo, node smoke-test.mjs replays the sequence as three checks — build, MCP handshake, registry — and exits non-zero on the first failure.
One DTCG file in, tokens.css out — 139 custom properties plus a dark block generated from the token file itself. The build scores nine text pairs for WCAG contrast in both themes and exits non-zero if one drops below 4.5:1. No Style Dictionary, no config, no dependencies.
The stdio MCP server exposes list_components, get_component, and get_tokens. Now "add a destructive button" is a lookup, not a guess — and asking for a component that doesn't exist gets back the list of ones that do.
Two filled-in templates with TODO markers where the decisions are yours. Including the two sections nobody writes: what your system deliberately does not cover, and what is deprecated — because an agent trained on your old docs will reach for it otherwise.
Replace the primitive brand ramp, re-run the build, read the contrast report, commit. There is no npm package to depend on and no service to subscribe to.
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,
"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,
"method":"tools/list"}' \
| node mcp/server.mjsIf it answers, the server is wired correctly. This exact command is in the README — and node smoke-test.mjs runs it for you, alongside the token build and a registry check.
What the agent gets back
list_components, get_component, get_tokens. Ask for a component that doesn't exist and the error names every one that does, then tells the model not to invent a new one.Works immediately
Configure yourself
Tokens only
Copy tokens.css into any app and import it in the root layout.
Registry into an existing shadcn-style app
Copy ten files to their targets, install the six packages the items declare, import tokens.css.
Full five-signal wiring
Both of the above, plus the MCP entry in your agent config, the template TODOs, and Code Connect pointed at your Figma file.
Planning ranges, counted from the steps each scope involves. The same table ships in the zip as COMPATIBILITY.md.
Compatibility
mcp/server.mjs. The full matrix, with the file that sets each floor, ships as COMPATIBILITY.md.We audited 37 public design systems against five concrete signals, counting only first-party evidence. 20 of them ship none of the five. The highest score anyone reaches is 3 of 5, and exactly one system gets there. The docs sites are excellent. The surface an agent can query mostly doesn't exist.
Signals addressed
Systems scoring zero
Left: the signals the starter ships foundations for. Right: how many of the 37 audited systems ship none of it.
The two tall bars are bolt-ons: a server and a text file that sit beside a system. The three signals that touch the system itself are still in single digits — and those are the three this kit ships as working code.
Count of systems with confirmed first-party evidence for each signal, audited 2026-06-10. "unknown" and "n/a" are not counted as yes. Source: /ai-ready/systems.
The same stack the starter packages is live on this domain right now — inspect it before you put your name on any list.