Using Tanrei with AI agents
Connect Tanrei's MCP server, give a coding agent your product's UI rules in AGENTS.md, and keep it to the components and values of the default system.
A coding agent that writes your product’s interface needs what a person on your team needs: which components exist, what each one takes, and where every value comes from. Tanrei keeps all three in files its packages ship and in an MCP server, so an agent can look them up instead of guessing. This page shows how to connect them, and gives you a rules file to copy into your repository.
- The components.
@tanrei/webships a Custom Elements Manifest,custom-elements.json: every element with its attributes, properties, events, slots and CSS parts, each with a description.@tanrei/reactships types for the same components, so a prop that does not exist fails type checking. - The values. Every token is a custom property in
@tanrei/web/tokens.css, named after its path:tanrei.sys.color.primaryis--tanrei-sys-color-primary. If your product has its own layer, thetokensoutput oftanrei buildis a DTCG bundle whose groups carry usage guidance for agents in$extensions["dev.tanrei.llm"](Your own design system). - What went wrong. A component that is used wrongly still renders, and in a development build it writes a warning to the console that starts with
[tanrei]and names the element, the problem and the rule it breaks: a button with neither a label nor an icon, a word your product has not given, a component the system leaves out. A production build (NODE_ENV=production) writes none.
@tanrei/mcp is a
Model Context Protocol server for Tanrei’s tokens and design-system layers. An agent asks it which tokens exist, what a token resolves to in a system and why, what a layer may change and how, and whether an edit to a layer is valid before it writes the edit. Every tool is read-only: the agent edits your layer file itself, and the server checks the edit with the same build tanrei build runs. Components are not among its answers; their contract is the manifest above.
Prerelease
0.1.0-next.0 is published to npm under the dist-tag next: until 0.1.0, install @tanrei/mcp@next. During the 0.x prereleases backward compatibility is not kept: any prerelease may break anything, with no deprecation period first, and its changeset says what broke.
Keep @tanrei/mcp on the same version as @tanrei/web, @tanrei/cli and the other libraries: they share one vocabulary.
In Claude Code, run this in your repository:
claude mcp add --scope project tanrei -- npx -y @tanrei/mcp--scope project writes the server into .mcp.json at the repository’s root. Commit that file, and everyone who works in the repository has the same server.
Cursor (.cursor/mcp.json), Claude Desktop and most other clients read the same server from JSON, over stdio:
{
"mcpServers": {
"tanrei": {
"command": "npx",
"args": ["-y", "@tanrei/mcp"]
}
}
}VS Code reads .vscode/mcp.json, whose servers stand under servers and name their transport:
{
"servers": {
"tanrei": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@tanrei/mcp"]
}
}
}The server needs Node.js 20 or newer. It resolves a relative layerFile from its working directory, which is normally the repository’s root.
Its tools, and what each one answers:
list_systems: the systems that ship with Tanrei and, given your layer file, your product’s chain of layers, with what each one adds to the vocabulary and its accessibility findingslist_components: the components a system ships, from its components manifest, each with the module that registers it and the variant values the system offers, and what the system does not have, with what to use instead; an agent calls it firstget_component: one component as its spec describes it, for one system: its props, events, slots and states, its behaviour as rules with stable ids, its accessibility and an examplefind_component: the components of the system that do a job you describe in plain words, best first, and apart from them those that match but the system does not have, with what to use insteadcheck_usage: every element, import andvariantin your code that the system does not have, with its file, line and column and what to use instead; it writes nothingsearch_tokens: tokens found by id, by a substring or a glob, with each token’s type, kind and descriptionresolve: what a token resolves to in a system and scheme, through which aliases and rules, set by which layer, and its CSS custom propertyexplain_token: why a token exists, what follows it, the changes a layer may write for it and how to add vocabulary next to it, each as a fragment of a layerdiff_systems: the tokens whose values differ between two systemsvalidate_layer: every problem of a layer at once, each with the way out, then the real build and the warnings it prints; it writes nothingpropose_extension: a new step, size, variant or token of your own as a fragment of a layer, checked by a trial build, with the values it makesmimicry_gaps: the traits of other design systems that no token can express yet
Its resources, and what each one holds:
tanrei://philosophy: the ideas behind the vocabulary and the rules a layer follows, on one pagetanrei://schema/system: the JSON Schema of a layer filetanrei://systems/<id>: each shipped system’s layer file as it is writtentanrei://components/<id>: each shipped system’s components manifest, as the repository generates it
The server also gives the agent its rules when it connects: use no component the system’s manifest does not list, change values through a layer, add vocabulary only under vocabulary and with a rationale, give every pinned value a reason, never change an accessibility threshold, and never edit a generated file. These two lists are the server’s own: a check holds this page to the tools and resources the server has, in every language, so a tool the server gains is added here with it.
Many coding agents read AGENTS.md at the root of a repository. Cursor does. Claude Code reads it when the repository has no CLAUDE.md; when it has one, add a line @AGENTS.md to it, and Claude Code reads both. Copy this section into your AGENTS.md:
## UI
This product's interface is built with Tanrei (https://tanrei.chnotchy.com), on its default design system, `tanrei`.
- Write UI with Tanrei's components. Before you build a control, a menu, a dialog, a list or a navigation element by hand, look for the component that does it: node_modules/@tanrei/web/custom-elements.json lists every element with its attributes, properties, events, slots and parts, and https://tanrei.chnotchy.com/components/ has a page for each.
- Use a component through what its manifest lists. Do not restyle it from outside and do not copy its markup. A look the components do not have is a change to the design system, not to one screen: ask before you make it.
- Take every color, radius, spacing, text style, elevation, motion and opacity from a token: var(--tanrei-sys-…) in CSS, for example --tanrei-sys-color-primary, --tanrei-sys-space-space200, --tanrei-sys-shape-corner-medium, --tanrei-sys-typescale-body-font. Never write a color, a length, a duration or a font for these yourself.
- Stay on the default system. Do not set data-tanrei-system, and do not import @tanrei/web/systems/*.css. Do not use a component, or a variant of one, that the system leaves out: its page says "Not in tanrei", or lists the variant under "Variants not shipped in", and in a development build it warns.
- A console message that starts with [tanrei] in a development build is a bug. Fix what it names before you finish.
- Give the product's words once, with setWords() from @tanrei/web: Tanrei ships none.
- Keep every @tanrei/* package on the same version.The rules are in English, the language of the names in the code; translate them if your team keeps its rules in another language. If your product has its own layer, replace the rule “Stay on the default system” with these two, using your layer’s name and file:
- Our design system is the layer design/acme.system.json, which extends tanrei. Change a value there, never in a generated file: check the edit with the tanrei MCP server's validate_layer before you write it, and run npx tanrei build after.
- Set data-tanrei-system="acme" once, on <html>, and no other system anywhere.Cursor also reads project rules from .cursor/rules/*.mdc; a rule with alwaysApply: true can hold the same text, if you keep your rules there instead.
The default system, tanrei, leaves a few components and variants out, which other shipped systems keep. They still render and work under it, so what keeps an agent from reaching for one is the rule above and the signals below. Today the default system leaves out:
<tanrei-segmented-button><tanrei-text-input variant="filled"><tanrei-text-input variant="outlined"><tanrei-select variant="filled"><tanrei-select variant="outlined"><tanrei-combobox variant="filled"><tanrei-combobox variant="outlined"><tanrei-number-input variant="filled"><tanrei-number-input variant="outlined"><tanrei-color-input variant="filled"><tanrei-color-input variant="outlined">
What tells an agent, and whoever reviews its work, that it reached for one:
-
Each component’s page says, under its overview, which systems do not ship it, or which of its variants they leave out.
-
In a development build, each element the active system leaves out warns once in the console, when it connects and when its
variantchanges. -
Your product’s own browser tests can hold the line: fail a page on any
[tanrei]warning. With Playwright:import { expect, test } from '@playwright/test'; test('the home page draws no Tanrei warning', async ({ page }) => { const warnings: string[] = []; page.on('console', (message) => { if (message.text().startsWith('[tanrei]')) warnings.push(message.text()); }); await page.goto('/'); expect(warnings).toEqual([]); });Run it against a development build: a production build writes no warning.