Exposing a design system to agents is now the minimum that teams expect. The harder questions are what happens when the system is partly wrong, and who notices when agents bypass it. That is where Loom works.
An agent needs three things to stay on-system
Agent readiness has three layers: retrieval (finding the right part of the system), rules (knowing what is allowed) and a verifier (checking the result). Loom provides all three through one MCP server. MCP is the Model Context Protocol, an open standard that lets an AI assistant call tools.
An agent's loop with Loom
Diagram- Search the system
search_system - Read the contract
get_entity - Write the interfacein the agent's own editor
- Check the composition
check_composition - Fix what was flaggedthe check names the fix
- Propose what is missing
intake_needorpropose_change
Retrieval finds the right component, not a similar one
search_system finds components, tokens and patterns and reports how confident it is. When nothing fits, it says "no strong match" instead of returning the closest guess. get_entity returns the full contract with the exact import, properties, states and accessibility notes. get_tokens returns token values and roles. Working
Every call in one recorded session
Recorded server output| Tool | What the server answered | Time |
|---|---|---|
search_system | 20 results for "delete confirm danger". | 145 ms |
get_entity | Button: Starts an action in the current view: save, publish, open a dialog, run a check. | 34 ms |
check_composition | Off-system: 3 errors, 2 warnings across 6 nodes. | 48 ms |
check_composition | On-system: 0 errors, 0 warnings across 5 nodes. | 5 ms |
audit_code | Drift found: 11 errors, 11 warnings — 7 colours, 9 dimensions, 1 non-system import, 1 re-implemented component. | 117 ms |
get_guidelines | Guidelines for illustration: missing. | 11 ms |
propose_change | Draft component-filedropzone-6af1d4b4 written to the drafts folder (status: proposed, labelled AI-generated, not yet human-reviewed). | 399 ms |
check_composition | Off-system: 4 errors, 5 warnings across 6 nodes. | 69 ms |
search_system | No component matches "date range picker". | 17 ms |
search_system | 51 results for "show an error when payment fails". | 14 ms |
list_unmet_needs | 1 unmet need from 1 request; 0 over the threshold (3 requests from 2+ requesters in 30 days). | 5 ms |
Search can also use your own embeddings model for meaning-based matching. Partial What does not work yet: it has been tested against a local test model only, not a real provider.
Rules tell the agent what is allowed
get_rules lists every rule, whether Loom or your linter enforces it, its exceptions and its open questions. Conversion also writes an AGENTS.md file and rules for your system: 251 rules for our reference system. Working
Tools that read Markdown get one file
Some tools read a single file rather than call a server. get_design_md returns your system as one DESIGN.md file, in the open DESIGN.md format: tokens under your own names, components, patterns, rules, brand and voice, and how agents should use the system. It is generated from the same records as the other tools, and it says "not defined" wherever a record is missing. Here is one entry from Loom Base's own file:
One entry from a real DESIGN.md
Loom Base, MIT# The Loom Base design system (@loom/base), exported by Loom from its records: 2312 tokens, 69 components, 11 patterns.
### Button
`import { Button } from "@loom/base/native";` · lifecycle beta · tier component
- **Use when:** To start an action that changes data or opens something on this page; To submit a form (set it as the form's last control); As a toggle (pressed) for an on/off f…
- **Do not use when:** Going to another page or URL → Link; Choosing one of several options → RadioGroup or Select; Turning a setting on or off that takes effect immediately → S…
- **Variants:** `tone`: primary | secondary | quiet | danger. primary: the one main action in a view; secondary: other actions; quiet: toolbars and dense areas; danger: removes …
- **Keyboard:** `Enter` PRESS; `Space` PRESS; `Tab` A disabled button is skipped; a busy one is notpackages/base/DESIGN.md, which Loom exported from the system's records, when this page is built. Lines are shortened here.The same file can be written into your repository from the command line, with a check that fails when the file is out of date. Working
What does not work yet: the file holds only the default mode's token values, and Loom does not yet deliver it to your repository as a pull request. For building interface, the other tools give more exact and more current answers than any single file.
The verifier checks the work before hand-off
check_composition checks a draft before it is written: raw values, rebuilt components, unlabelled controls, two primary actions in one region, style overrides and pattern rules. audit_code finds drift in existing files, including use of deprecated items. Both apply your governance: severities per area, promotions and exceptions. Working
Report a need instead of inventing a component
intake_need decides whether a need is reuse, extend or new. When nothing matches, the need is recorded for the unmet-needs radar. list_unmet_needs shows needs grouped and counted against the owner's threshold. propose_change writes a draft with the alternatives considered, labelled as AI-generated and not yet reviewed. Partial
What does not work yet: drafts created automatically once a need passes the threshold. Concept
Recording of real output
A recorded session: a scripted agent against Loom Base, our own design system (MIT), with real server output. The replay types each call as the agent sent it and shows each result after its recorded latency, shortened for the page. Server output is in English.
- Agent
I need a destructive "delete workspace" action with a confirmation. What does the system have?
- Loom ·
search_system· 145 ms→ search_system({"query":"delete confirm danger","kind":"component","limit":5})20 results for "delete confirm danger" (lexical ranking). Top: Dialog (dialog, 0.8, beta), ConfirmAction (confirm-action, 0.46, beta), ToastRegion (toast-region, 0.41, beta), Tag (tag, 0.27, beta), Alert (alert, 0.25, beta)
• Dialog (dialog, strong, confidence 0.8) — import { Dialog } from "@loom/base/native";• ConfirmAction (confirm-action, partial, confidence 0.46) — import { ConfirmAction } from "@loom/base/native";• ToastRegion (toast-region, partial, confidence 0.41) — import { ToastRegion } from "@loom/base/native"; - Loom ·
get_entity· 34 ms→ get_entity({"id":"Button"})Button: Starts an action in the current view: save, publish, open a dialog, run a check.
- Agent
Here is my first draft. It reaches for an MUI Modal, guesses a variant prop, uses two primary buttons and hard-codes a colour and a margin.
- Loom ·
check_composition· 48 ms→ check_composition({"tsx":"import { Card, Button, Heading, Text } from \"@loom/base/native\";\nim…"})Off-system: 3 errors, 2 warnings across 6 nodes.
- [warning] Text has style set: overriding a system component's styling is drift from its contract. → Customise through the component's slots, variants or overridable tokens wit…- [error] Button has no "variant" prop. → Did you mean "tone" ("primary" | "secondary" | "quiet" | "danger")? primary: the one main action in a view; secondary: other actions; q…- [error] <Modal> is not part of Loom Base (imported from "@mui/material"). → Closest system components: AILabel (ai-label), Dialog (dialog), Drawer (drawer). If none fits, draf… - Loom ·
check_composition· 5 ms→ check_composition({"tsx":"import { Card, Button, Text, Stack, ConfirmAction } from \"@loom/bas…"})On-system: 0 errors, 0 warnings across 5 nodes.
components: Card, Stack, Text, Button, ConfirmAction - Loom ·
audit_code· 117 ms→ audit_code({"files":[{"path":"src/settings/SettingsPage.tsx","source":"import React from \"react\";\nimport { Button as MuiButton, Dialog…Drift found: 11 errors, 11 warnings — 7 colours, 9 dimensions, 1 non-system import, 1 re-implemented component.
✗ src/settings/settings.css:2 Hard-coded colour #fffdf8 (background). → Use var(--base-color-palette-cotton-0) ().✗ src/settings/settings.css:5 Hard-coded dimension 16px (padding). → Use var(--base-space-scale-6) (16px).✗ src/settings/settings.css:5 Hard-coded dimension 20px (padding). → Use var(--base-space-scale-7) (20px). - Loom ·
get_guidelines· 11 ms→ get_guidelines({"topic":"illustration"})Guidelines for illustration: missing. 0 sections. Missing: Loom Base has no dedicated illustration guideline. Do not invent one: follow existing foundations, and use propose_change (kind "guideline") to draft it for review.
missing: Loom Base has no dedicated illustration guideline. Do not invent one: follow existing foundations, and use propose_change (kind "guideline") to draft it for review. - Agent
Research uploads need a drag-and-drop file area. Nothing in the system does that, so I will draft a proposal rather than ship a one-off.
- Loom ·
propose_change· 399 ms→ propose_change({"kind":"component","title":"FileDropzone","need":"Researchers attach interview recordings and notes to a journey. Set…","…Draft component-filedropzone-6af1d4b4 written to drafts/component-filedropzone-6af1d4b4.json (status: proposed, labelled AI-generated, not yet human-reviewed). Intake: extend. 7 existing alternatives listed; impact: 5 declared dependents.
impact: 5 declared dependents (partial graph)status: proposed; system source modified: false - Loom ·
check_composition· 69 ms→ check_composition({"tsx":"import { Modal } from \"@mui/material\";\nimport { Button } from \"@loo…"})Off-system: 4 errors, 5 warnings across 6 nodes.
- [warning] Raw <h1> used where the system provides Heading. → import { Heading } from "@loom/base/native";- [error] Button has no "variant" prop. → Did you mean "tone" ("primary" | "secondary" | "quiet" | "danger")? primary: the one main action in a view; secondary: other actions; q…- [error] Button has no "variant" prop. → Did you mean "tone" ("primary" | "secondary" | "quiet" | "danger")? primary: the one main action in a view; secondary: other actions; q… - Agent
The booking flow needs a date range picker. Does the system have one?
- Loom ·
search_system· 17 ms→ search_system({"query":"date range picker for booking","kind":"component","limit":5})No component matches "date range picker". Closest partial matches: ComboBox (combo-box, partial match, confidence 0.24, beta: covers "picker" but not "date range"); FormField (form-field, partial match, confidence 0.24, beta: covers "picker" but not "date range"); LooseThreads (loose-threads, partial match, confidence 0.24, beta: covers "picker" but not "date range"). Logged as an unmet need. If this is a real gap, draft it with propose_change instead of building it off-system.
• ComboBox — partial match: covers "picker" but not "date range"• FormField — partial match: covers "picker" but not "date range"• LooseThreads — partial match: covers "picker" but not "date range" - Loom ·
search_system· 14 ms→ search_system({"query":"show an error when payment fails","kind":"component","limit":3})51 results for "show an error when payment fails" (lexical ranking). Top: ErrorSummary (error-summary, 0.93, beta), AIStatus (ai-status, 0.74, beta), Alert (alert, 0.74, beta)
• ErrorSummary (error-summary, strong, confidence 0.93) — import { ErrorSummary } from "@loom/base/native";• AIStatus (ai-status, strong, confidence 0.74) — import { AIStatus } from "@loom/base/native";• Alert (alert, strong, confidence 0.74) — import { Alert } from "@loom/base/native"; - Loom ·
list_unmet_needs· 5 ms→ list_unmet_needs({})1 unmet need from 1 request; 0 over the threshold (3 requests from 2+ requesters in 30 days). "date range picker" x1.
• "date range picker": 1 request(s), 1 requester(s) — Below threshold: needs 2 more request(s) and 1 more client(s) or team(s) within 30 days.
Content from your repository is treated as data
Tool results quote your repository, docs and Figma. Loom marks that content as untrusted, neutralises it and tells the agent it is data, not instructions. This lowers the risk of prompt injection through your own files. It does not remove it. Working
Your model, your keys
Any agent that speaks MCP can use your system with the model you choose. Working For agents that Loom runs for you, provider setup for Anthropic Claude, OpenAI and any OpenAI-compatible gateway is in development. Concept
Loom's current tools do not call a model. They are deterministic checks, so they give the same answer whichever model produced the result.
It runs where you choose
The command line and the MCP server run on your own machines today. Partial What does not work yet: a packaged CI step and Loom cloud are planned.
Over a network, the server requires a secret token of at least 32 characters on every request and refuses to start without one. Working This is a shared secret, not the MCP authorisation standard (OAuth), so hosted assistants that need OAuth cannot connect yet.
Every run has a budget and a record
Every tool call is logged with its duration, token counts and a cost estimate. You can cap a run by calls, tokens or cost. Working Every draft an agent writes is labelled. Working
Choose a model on evidence, not on reputation
Contract tests for agents would run the same tasks and checks for different models, so you can choose the model that builds best with your system. Concept
An agent is only as good as the system it can read. We make sure it reads the real one.
Questions
We already built our own MCP server. Why use Loom's? Then you are ahead of most teams. Loom adds the verifier, the governance behind it (owners, severities, exceptions) and the record of unmet needs.
Which assistants work with it? Any MCP client that connects over standard input and output or local HTTP. Remote clients that need OAuth cannot connect yet.
How fast is it? In the recorded session on Loom Base, the 11 calls took between 5 and 399 milliseconds.
Does an agent ever merge its own work? Not today. Every change waits for a person. Autonomy modes that allow more are a concept.