Built to be written by agents
Every component has a machine-readable contract, and the same build turns it into a prompt fragment, a linter that suggests fixes, an MCP server, types, and this site. They are generated from one schema, so they cannot disagree: if the linter accepts markup, the prompt taught it and the docs show it.
Connect an agent
The package ships a local stdio server, started with aihio mcp. Add it to the client that writes
your UI: npx runs the project's copy of @luntta/aihio when it is installed, and
fetches the package otherwise.
Claude Code
claude mcp add aihio -- npx -y @luntta/aihio mcp
Any client with a JSON config
Claude Desktop, Cursor, and other clients with an mcpServers config take the same entry.
VS Code's .vscode/mcp.json takes it under servers instead.
{
"mcpServers": {
"aihio": {
"command": "npx",
"args": ["-y", "@luntta/aihio", "mcp"]
}
}
}
Without MCP
Put the prompt fragment in the system prompt or the project's instructions, and lint what comes back.
import prompt from '@luntta/aihio/prompt';
import { lintMarkup } from '@luntta/aihio/lint';
MCP tools
Read from the server itself. It also serves the prompt fragment as the prompt aihio-authoring.
find-
query - Start here. Given what the UI has to do — free text such as "confirm before deleting a project", or an intent name such as "destructive-action" — return the best-matching components and canonical patterns. Adapt a returned pattern with get_pattern rather than composing from scratch.
list_components- List every top-level Aihio component with its one-line purpose, intents, commands, and subcomponents.
list_patterns- List the canonical multi-component patterns (auth form, settings section, destructive confirmation, ...) with their purpose and intents.
get_pattern-
id - Return a canonical pattern's complete, lint-clean markup and its variations. Adapt it instead of freehanding the structure.
describe-
component - Return the schema entry for a component tag such as "
aihio-button" or "button". lint-
markupsource? - Validate markup against the Aihio schema and return structured issues. Run it on every snippet before returning it; an issue with a "suggestion" can be fixed by applying that replacement.
What find returns
Run at build time, for "confirm before deleting a project". Scores are trimmed from the full entries.
{
"components": [
{"tag":"aihio-dialog","score":8},
{"tag":"aihio-alert","score":2},
{"tag":"aihio-button","score":1}
],
"patterns": [
{"id":"destructive-confirmation","score":6},
{"id":"auth-form","score":1},
{"id":"data-table","score":1},
{"id":"empty-state","score":1},
{"id":"inline-form-validation","score":1}
]
}
The lint loop
Lint every snippet before returning it. An issue with a suggestion is fixed by applying the
suggestion, so a model needs one round trip, not a reading of the docs. This is the linter's real output.
1. Markup in the shape of other design systems
<aihio-button variant="primary" href="/pricing">See pricing</aihio-button>
2. lintMarkup() returns
[
{
"ruleId": "unknown-attribute",
"severity": "error",
"message": "<aihio-button> has no attribute \"href\": a button does not navigate. Put the link inside it, and it becomes the control, styled as the button: <aihio-button variant=\"primary\"><a href=\"/pricing\">See pricing</a></aihio-button>",
"suggestion": "<aihio-button variant=\"primary\"><a href=\"/pricing\">See pricing</a></aihio-button>"
},
{
"ruleId": "invalid-enum-attribute",
"severity": "error",
"message": "invalid variant=\"primary\". Use variant=\"default\". Expected one of: default, secondary, outline, ghost, link, destructive.",
"suggestion": "variant=\"default\""
}
]
3. Both suggestions applied
0 issues. The link is a real link, drawn as the button.
<aihio-button variant="default"><a href="/pricing">See pricing</a></aihio-button>
Files to read
Everything ships in the package. This site also serves llms.txt, llms-full.txt, and every component page as markdown (components/button/index.md), which the "Copy for agent" button on each component page copies.
dist/aihio.prompt.md-
import
@luntta/aihio/prompt.md64 KB - The prompt fragment: rules, every component, the intent map, patterns, obligations, and each mistake beside its fix.
dist/schema.json-
import
@luntta/aihio/schema186 KB - The full schema: components, sub-components, intents, patterns, examples, and counterexamples.
dist/schema.min.json-
import
@luntta/aihio/schema/min90 KB - The same, with the prose stripped, for a tight context budget.
dist/semantic-tokens.md-
import
@luntta/aihio/tokens.md12 KB - Every semantic token, its source, and what it is for.
dist/tokens.json-
import
@luntta/aihio/tokens56 KB - Every token in every tier, with resolved light and dark values and the contrast contract.
dist/aihio.d.ts-
import
@luntta/aihio24 KB - Types generated from the schema, including JSX intrinsic elements.
In the browser
The rules an agent reads are the ones the page enforces while you build.
aihio/dev- The same components, with console warnings for invalid attribute values and broken accessibility obligations, checked by the same rules as the linter. The production bundle carries none of it.
Aihio.describe(tag)- A component's schema at runtime, for tools that inspect a live page.
data-aihio-intent- Says what an element is for, from the intent vocabulary. Optional, but the linter rejects an annotation the component's schema does not back.
dist/aihio.d.ts- Types generated from the schema: attribute unions, intents, and JSX intrinsic elements.