ds-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ds-mcp-serverwhat token should replace #1a73e8?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ds-mcp-starter
A contract-first Model Context Protocol server for a design system.
It reads DTCG design tokens, indexes them into a typed, deterministic manifest, and exposes the same pure core through three facades: an MCP server for LLM agents, a CLI for humans and CI, and an importable TypeScript library.
The repository ships a small fictional multi-brand system (brands aurora and nord, modes light and dark, 81 tokens per theme) so every command below runs as-is.
Contract first, MCP second
MCP is a transport. The value lives in the contract underneath it: a manifest with a schema, and a set of pure functions that answer design system questions from it.
The indexer turns token files into
dist/manifest.json(plus its JSON Schema). No network, no clock, same input gives the same bytes.The core is a set of pure functions over that manifest, each with a zod input and output schema.
The facades are thin. The MCP server registers the core functions as tools; the CLI prints them for people and returns exit codes for CI; the library exports them.
CI never starts an MCP server. It runs the same code through the CLI.
flowchart LR
subgraph Sources
T[tokens/**/*.json<br/>DTCG 2025.10]
end
T --> I[Indexer<br/>flatten, resolve aliases,<br/>detect cycles]
I --> M[(Manifest<br/>dist/manifest.json<br/>+ JSON Schema)]
M --> C[Core tools<br/>pure functions<br/>zod in / zod out]
C --> S[MCP server<br/>stdio]
C --> L[CLI ds<br/>humans + CI gates]
C --> B[Library<br/>import from ds-mcp-starter]
S --> A[Any MCP client<br/>LLM agents]Related MCP server: Designesy
Tools
Tool | Access | Answers |
| read | Which token expresses this intent? Ranked search on paths and descriptions; semantic before primitive, deprecated last. |
| read | What does this token become in |
| read | Which token should replace this hard-coded |
| read | Does this name follow |
| read | Where does this CSS hard-code values, use deprecated tokens or reference tokens that do not exist? With line, column and suggestions. |
| read | Is this change set a major, minor or patch release, and why? |
| write | What exactly would adding this token change? Returns a JSON Patch and a unified diff; writes only on explicit confirmation. |
Tool descriptions are written for an LLM: when to call the tool and what it returns. Read-only and write tools are flagged with MCP annotations (readOnlyHint, destructiveHint).
Governance: AI prepares, humans decide
Agents are fast at preparing changes and bad at owning them. The server is built so that the default path is always a reviewable proposal.
Write tools preview by default.
propose_tokenreturns the target file, a JSON Patch and a diff.confirm: trueis required to write.Two keys for writes over MCP. Even with
confirm: true, the MCP server refuses to write unless it was started with--allow-writes. The intended flow is: the agent previews, a person runsds propose ... --confirmor applies the diff in a pull request.Validation before preview. A proposal is rejected before any diff is produced if the name breaks the grammar, collides with an existing token or group, aliases a token that does not exist in every affected theme, or mixes types.
Naming lint. One grammar for people and agents. CSS property names (
padding), brand or mode names (aurora,dark) and misplaced states are rejected with a suggested fix.Semver arbitration. Removed or renamed tokens are majors, additions and deprecations are minors, value changes are patches unless the type changes. Removing a token that was never deprecated produces a warning: deprecate before you remove.
CI gate.
ds auditfails the build on hard-coded values or unknown tokens;ds semver --max minorfails a change set that would require a major.
Quickstart
Requires Node.js 20 or later.
npm install
npm run build # compile TypeScript to dist/
npm run manifest # write dist/manifest.json and dist/manifest.schema.json
npm link # optional: puts the ds and ds-mcp-server commands on your PATHWithout npm link, replace ds with node dist/cli/main.js below.
CLI
ds search "secondary text"
ds resolve color.text.primary --brand aurora --mode dark
ds find "#1a73e8" --brand nord --mode light
ds lint color.fg.subtle spacing.padding.md color.dark.surface
ds audit examples/button.css examples/legacy.css
ds semver examples/changes.json --max minor
ds propose color.fg.subtle --type color --value "{color.neutral.500}" \
--description "Placeholder text and disabled labels." --brand aurora --mode lightEvery command accepts --json. Exit codes: 0 success, 1 a check failed (lint, audit, semver above --max, rejected proposal), 2 usage or data error.
$ ds resolve color.text.primary --brand aurora --mode dark
color.text.primary (aurora/dark) = #f7f8fa
chain: color.text.primary -> color.fg.default -> color.neutral.50
css: var(--color-text-primary)
type: color, semantic, brands/aurora/modes/dark.json
about: Former name of the default text color.
status: deprecated. Use {color.fg.default}. Scheduled for removal in 2.0.0.
$ ds audit examples/legacy.css --brand nord --mode light # excerpt
examples/legacy.css:3:12 error hard-coded-dimension padding: Hard-coded dimension 16px. Use var(--spacing-inset-md).
examples/legacy.css:5:21 error hard-coded-color border: Hard-coded color #dde1e8. Nearest token: var(--color-border-subtle).
examples/legacy.css:14:14 warning deprecated-token color: --color-text-primary is deprecated. Use {color.fg.default}. Scheduled for removal in 2.0.0.
examples/legacy.css:22:14 error unknown-token color: --color-fg-subtle looks like a design token but does not exist. Did you mean --color-bg-subtle, --color-fg-muted, --color-bg-surface?
examples/legacy.css: 14 error(s), 1 warning(s)MCP client
Any MCP client that launches stdio servers can use it. See examples/mcp-client-config.json:
{
"mcpServers": {
"design-system": {
"command": "node",
"args": ["/absolute/path/to/ds-mcp-starter/dist/server/main.js", "--tokens", "/absolute/path/to/ds-mcp-starter/tokens"]
}
}
}Server options: --tokens <dir> (default tokens, or $DS_TOKENS_DIR), --manifest <file> to serve a prebuilt manifest in read-only mode, --allow-writes to let propose_token write on confirmation. Logs go to stderr.
Library
import { buildManifestFromDirectory, findTokenForValue, auditCss } from "ds-mcp-starter";
const manifest = await buildManifestFromDirectory("tokens");
findTokenForValue(manifest, { value: "#1a73e8", property: "background" }).recommendation;
auditCss(manifest, { css: ".a { padding: 16px }" }).ok; // falseToken layout
tokens/
core/*.json primitives shared by every theme (color scales, spacing, radius, font, border)
semantic/*.json semantic tokens shared by every brand (spacing.inset.md -> spacing.4)
brands/<brand>/brand.json semantic tokens of one brand (radius.control, font.family.brand)
brands/<brand>/modes/<mode>.json semantic colors of one brand in one modeA theme is one brand in one mode: core + semantic + brand + mode, merged in that order. A path may be defined only once per theme. Values follow DTCG 2025.10 ({ "colorSpace": "srgb", "components": [...], "hex": ... } for colors, { "value": 16, "unit": "px" } for dimensions), aliases use {path} and $deprecated names its replacement. In code, paths are dot-separated (color.fg.default); in CSS they become --color-fg-default.
Project structure
src/
core/ pure: DTCG parsing, alias resolution, manifest schema, and the 7 tools
tools/ one file per tool: zod input, zod output, pure function
node/ the only file system code: discover token files, build/read/write the manifest, apply proposals
tools/ facade-agnostic tool registry (name, LLM-facing description, schemas, read/write)
server/ MCP facade: registers the registry on an McpServer, stdio entry point
cli/ CLI facade: argument parsing, human formatting, exit codes
index.ts library facade
tokens/ demo DTCG tokens (2 brands x 2 modes)
examples/ MCP client config, clean and legacy CSS, a semver change set
scripts/ stdio smoke test of the built server
test/ Vitest suites, including an in-memory MCP end-to-end testTesting
npm test # 63 tests
npm run typecheck
npm run lint # Biome
npm run smoke # starts dist/server/main.js over stdio and calls a tool
npm run check # everything CI runs, in orderThe suites cover alias resolution (multi-level chains, cycles, unknown references, type mismatches, duplicates), value serialization, search ranking, nearest-color matching, every naming rule, CSS audit positions and suggestions, semver rules, proposal preview versus confirmation, and an end-to-end MCP session through the SDK client over an in-memory transport. Two repository guards run with them: no stdout writes in server-side modules, and token files kept in the canonical format the tools write. The demo tokens are also linted with the naming grammar, so the example system follows its own rules.
Design decisions
Contract first. The manifest has a zod schema, published as JSON Schema next to it. Consumers validate what they read. The MCP tools publish input and output schemas and return
structuredContentvalidated against them.One core, three facades. Business rules live in
src/coreonly. The MCP server and the CLI only parse input and format output, so behavior is tested once, without starting a server.No network in the indexer. Token sources are local files; the indexer is a pure function from sources to manifest. Deterministic output (sorted, no timestamps) keeps the manifest diffable in code review.
stdout belongs to the protocol. The server logs to stderr only. A Biome rule (
noConsoleoutside the CLI), a test scanning server-side modules and a stdio smoke test protect the stream.Deprecate before you remove. Deprecations name their replacement (
$deprecated: "Use {color.fg.default}."). The audit reports usages with the replacement, search ranks deprecated tokens last, and semver arbitration warns when a token disappears without a deprecation period.Intent over value. Matching a value is not enough:
16pxis both a padding and a radius.find_token_for_valueandaudit_cssrank tokens by the intent of the CSS property and prefer semantic tokens over primitives.Small on purpose. Two runtime dependencies (
@modelcontextprotocol/sdk,zod), exact versions, no bundler.
Roadmap
Figma variables export: map brands and modes to variable collections and modes from the same manifest.
Code Connect hints: link components to the tokens they consume, so agents can answer "which token does this component use".
Streamable HTTP transport, for a shared read-only server in front of a published manifest.
Manifest diff: compute
arbitrate_semverinput from two manifests instead of a hand-written change set.Composite types (typography, shadow, gradient) and partial references.
License
MIT, Florian Cazé.
Available Tools
7 toolsarbitrate_semverArbitrate the semver bump of token changesARead-onlyIdempotent
Given the token changes of a release (added, removed, renamed, value-changed, deprecated), return the required version bump (major, minor, patch) with a reason per change, and warnings when a token is removed without having been deprecated first. Use it to prepare a release or review a change set.
| Name | Required | Description | Default |
|---|---|---|---|
| changes | Yes | Token changes included in the release |
Output Schema
| Name | Required | Description |
|---|---|---|
| bump | Yes | |
| changes | Yes | |
| warnings | Yes | |
| principle | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds real behavioral detail beyond that: a reason is returned per change, and a warning is emitted when a token is removed without prior deprecation — information an agent could not infer from the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, and the output contract is front-loaded before the usage hint. Every clause carries new information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description needn't restate return values, yet it still summarizes the bump, per-change reasons and warnings. Combined with a fully documented single parameter, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes further by enumerating the five change kinds (added, removed, renamed, value-changed, deprecated) that the `changes` array accepts, giving the agent the semantic vocabulary of the required input.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource — take token changes and return the required semver bump (major/minor/patch) with per-change reasons and warnings. That output shape makes it clearly distinct from siblings like lint_naming or audit_css, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear context for use: 'Use it to prepare a release or review a change set.' That is enough for an agent to know when it applies, but there are no exclusions or named alternative tools for adjacent tasks such as linting or proposing changes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
audit_cssAudit CSS for token usageARead-onlyIdempotent
Audit a CSS snippet: reports hard-coded colors and dimensions (with the token to use instead), usages of deprecated tokens (with their replacement) and references to tokens that do not exist. Returns ok=false when there is at least one error. Use it to review generated or legacy CSS.
| Name | Required | Description | Default |
|---|---|---|---|
| css | Yes | CSS source to audit | |
| mode | No | Restrict suggestions to one mode | |
| allow | No | Literal values that are accepted as-is | |
| brand | No | Restrict suggestions to one brand |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | False when at least one error was found |
| findings | Yes | |
| errorCount | Yes | |
| warningCount | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds genuine behavioral context beyond that: the categories of findings it emits and the ok=false contract when at least one error is present. It does not describe pagination or result ordering, but for a non-destructive, idempotent audit that is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero filler, front-loaded with the action and the enumerated findings, then the usage cue. Every clause carries information an agent needs for selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a 100% covered schema and an output schema present, the description only needs to cover purpose and behavior, which it does. It could be slightly stronger by clarifying how suggestions are scoped by mode/brand, but nothing essential for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so css, mode, brand, and allow are already documented in the schema. The description adds no semantics for mode/brand scoping or the allow-list behavior beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (audit) and resource (CSS snippet) and enumerates exactly what it reports: hard-coded colors/dimensions with replacement tokens, deprecated token usage, and nonexistent token references. This distinguishes it clearly from siblings such as lint_naming, search_tokens, and find_token_for_value.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use it to review generated or legacy CSS" gives clear when-to-use context. It does not name alternatives or state when-not to use it (e.g., versus lint_naming), so it falls just short of explicit routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_token_for_valueFind the token for a hard-coded valueARead-onlyIdempotent
Given a hard-coded value (#1a73e8, rgb(...), 16px, 1rem), return the tokens that carry it: exact matches first (semantic before primitive), then the perceptually nearest colors or nearest dimensions. Use it whenever code or a design contains a literal value, to replace it with a token.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Color mode, for example light or dark | |
| type | No | Inferred from the value when omitted | |
| brand | No | Brand name, for example aurora or nord | |
| limit | No | ||
| value | Yes | A hard-coded value, for example #1a73e8, rgb(26 115 232), 16px or 1rem | |
| property | No | CSS property where the value is used (padding, color, border-radius...). Ranks tokens by intent |
Output Schema
| Name | Required | Description |
|---|---|---|
| type | Yes | |
| exact | Yes | |
| value | Yes | |
| nearest | Yes | |
| normalized | Yes | Canonical form of the input used for comparison |
| recommendation | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish the read-only, idempotent, closed-world safety profile, and the description adds real behavioral detail beyond them: the result ordering (exact → semantic → primitive) and the fuzzy fallback to perceptually nearest colors or dimensions. It never states implied limits on the fallback (e.g. how distant a match can be) or whether approximate results are flagged.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero waste, with the matching strategy front-loaded ahead of the usage cue. The parenthetical format examples mildly duplicate the schema's value description but are defensible as quick-scannable input hints.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shape needn't be explained, and the description covers the input formats and result ranking that matter for correct invocation. Gaps remain around how brand/mode/property constrain matching and how the limit interacts with the fuzzy fallback tier.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 83%, so the schema already documents mode, type, brand, property and limit. The description only reinforces the value formats, which are also examples in the schema, and its ranking language hints at property-as-intent without making that explicit. It adds little beyond the baseline for the five optional parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource pair: given a hard-coded literal, return the tokens carrying it, with an explicit match strategy (exact first, semantic before primitive, then nearest). Input formats are enumerated. It doesn't name sibling tools like search_tokens or resolve_token, so an agent must infer the boundary itself.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Use it whenever code or a design contains a literal value, to replace it with a token" gives a clear activating condition. There are no exclusions and no named alternatives, so it stops short of the when/when-not/alternative guidance that would earn a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
lint_namingLint token namesARead-onlyIdempotent
Check proposed token names against the naming grammar category.role.variant[.state]: lowercase kebab segments, known category, intent words instead of CSS properties (inset, not padding), state last, no brand or mode in the name. Returns violations and a corrected name when one can be derived. Call it before proposing a token.
| Name | Required | Description | Default |
|---|---|---|---|
| names | Yes | Proposed token names as dot paths, for example ['color.fg.subtle', 'spacing.padding.md'] |
Output Schema
| Name | Required | Description |
|---|---|---|
| valid | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover safety (readOnlyHint, idempotentHint, destructiveHint=false), so the bar is lower. The description adds real behavioral value beyond that: it discloses the rule set being enforced and that the result is a violation list plus a derived corrected name 'when one can be derived', which is a meaningful caveat about non-determinism of the output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the tool's purpose, then packs the grammar rules densely without filler. The rule enumeration is long but each clause (kebab case, intent words, state last, no brand/mode) carries distinct enforcement information, so nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be re-explained, and annotations cover the safety profile. Combined with 100% parameter schema coverage, the description supplies everything an agent needs about intent, rules, timing, and result shape.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter, and schema description coverage is 100% — the schema already documents 'names' as dot-path strings with examples. The description's grammar text implies what valid input looks like but adds no parameter-specific syntax or formatting detail. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Check proposed token names') and enumerates the exact grammar being enforced (category.role.variant[.state], lowercase kebab, state last, no brand/mode). It does not explicitly name or contrast the other siblings such as audit_css or find_token_for_value, though 'Call it before proposing a token' does implicitly position it against propose_token.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear timing guidance: 'Call it before proposing a token.' That tells the agent when in the workflow to invoke it. It omits when-not-to-use and doesn't address the other siblings (e.g., when to lint vs. audit_css), so it falls short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
propose_tokenPropose a new tokenADestructive
Prepare the addition of a token. Validates the name (lint_naming), collisions, the scope and the value (aliases must exist and have the same type), then returns a preview: the target file, a JSON Patch and a unified diff. Nothing is written unless confirm is true AND the server was started with writes allowed. Default to a preview and let a human review it.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Scope the token to one mode of the brand. Requires brand | |
| path | Yes | Dot path of the new token, for example color.fg.subtle | |
| type | Yes | ||
| brand | No | Scope the token to one brand. Omit for a token shared by all brands | |
| value | Yes | An alias such as {color.neutral.500} (preferred for semantic tokens) or a literal: #1a73e8, 16px, 1.5 | |
| confirm | No | false (default) returns a preview and writes nothing. true writes the file, only after human review | |
| description | No | Intent of the token: when to use it. Search and agents rely on it |
Output Schema
| Name | Required | Description |
|---|---|---|
| diff | Yes | Unified diff of the target file |
| path | Yes | |
| patch | Yes | RFC 6902 JSON Patch applied to the target file |
| status | Yes | |
| target | Yes | Token file that receives the change, relative to the tokens directory |
| message | Yes | |
| warnings | Yes | |
| violations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations mark this destructive (readOnlyHint=false, destructiveHint=true, idempotentHint=false), and the description adds the crucial guardrail context beyond them: nothing is written unless confirm is true and the server was launched with writes enabled, plus the exact validation sequence and what the preview contains. It also tells the agent the intended workflow (preview, then human review), which annotations alone cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four tight sentences, front-loaded with the purpose, then the validation pipeline, then the write gate, then the recommended workflow. Every sentence carries information an agent needs and none repeats the schema or title verbatim.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having 7 parameters, an output schema and rich annotations, the description still covers the gaps that matter: alias typing rules, the confirm/writes-allowed gate, and how to treat the preview. Nothing needed to invoke it correctly is missing; return-shape detail is rightly left to the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 86%, so baseline is 3, but the description adds real meaning: value must be an alias that exists and shares the same type, and confirm is the hard gate for the write. It stays silent on brand/mode/description semantics, which the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Prepare the addition of a token') plus the resource and the exact scope of work: lint_naming validation, collision/scope/value checks, and a preview return of target file, JSON Patch and unified diff. This clearly distinguishes it from siblings like lint_naming (a validation used internally) and search_tokens/resolve_token (read-side tools).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operating context ('Default to a preview and let a human review it') and explains the condition that turns it into a write (confirm true AND server started with writes allowed). It does not explicitly name a sibling alternative or spell out when-not-to-use, so it falls short of the top tier but leaves little to infer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_tokenResolve a tokenARead-onlyIdempotent
Get the final value of one token for a given brand and mode, with the alias chain that produced it (for example color.fg.default -> color.neutral.900). Accepts a dot path, a CSS custom property or var(). Use it to explain a value or to check what a token becomes in dark mode.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | Yes | Color mode, for example light or dark | |
| path | Yes | Token reference: dot path (color.fg.default), custom property (--color-fg-default) or var(--color-fg-default) | |
| brand | Yes | Brand name, for example aurora or nord |
Output Schema
| Name | Required | Description |
|---|---|---|
| mode | Yes | |
| path | Yes | Dot path, for example color.fg.default |
| type | Yes | |
| brand | Yes | |
| layer | Yes | core = primitive values, semantic = intent-based tokens that alias core tokens |
| value | Yes | Resolved value, serialized for CSS |
| cssVar | Yes | CSS custom property name, for example --color-fg-default |
| source | Yes | Source file, relative to the tokens directory |
| rawValue | Yes | The $value exactly as authored (literal or {alias}) |
| aliasChain | Yes | Paths followed to resolve the value, nearest first. Empty for literal values |
| deprecated | Yes | false, true, or a message naming the replacement (DTCG $deprecated) |
| description | No | |
| replacement | No | Token to use instead, when this one is deprecated |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and openWorldHint=false, so safety and side-effect behavior are covered. The description adds meaningful context beyond annotations by disclosing that the result includes the alias chain that produced the final value, which helps an agent understand the return shape without relying solely on the output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core action and scoped by brand and mode, with a compact inline example. No wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Inputs are fully described in the schema, an output schema exists, and the description explains the resolution result including the alias chain. An agent has enough to call and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents accepted token reference forms (dot path, custom property, var()) and example brand/mode values. The description largely repeats what the schema states, adding only the dark-mode use case, so baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Get the final value of one token for a given brand and mode, with the alias chain.' The example (color.fg.default -> color.neutral.900) makes the output concrete and clearly distinguishes it from sibling search_tokens or find_token_for_value.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear contexts for use: 'to explain a value' and 'to check what a token becomes in dark mode.' It does not explicitly name alternatives or exclusions, but the stated scenarios are sufficient to guide selection among siblings for most cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_tokensSearch design tokensARead-onlyIdempotent
Find design tokens by intent or name, for example 'muted text', 'control radius' or 'danger'. Use it before writing any color, spacing, radius or font value. Returns ranked tokens with their CSS custom property, description, deprecation status and resolved value per brand and mode. Semantic tokens rank above primitives; deprecated tokens rank last.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Color mode, for example light or dark | |
| brand | No | Brand name, for example aurora or nord | |
| limit | No | ||
| query | Yes | Words to look for in token paths and descriptions, for example 'muted text' or 'radius control' |
Output Schema
| Name | Required | Description |
|---|---|---|
| query | Yes | |
| total | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds genuinely new behavior: ranking rules (semantic above primitives, deprecated last) and the returned fields per brand and mode, which an agent cannot infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with purpose then the usage imperative, then return/ranking behavior. No filler, though the ranking clause and return-field list are slightly dense for one sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be spelled out, yet the description adds ranking semantics that matter for interpreting results. Only the undocumented 'limit' parameter and pagination/truncation behavior are left unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75%; query, brand and mode are documented in the schema, though 'limit' has no description at all. The description reinforces the query style and implies brand/mode shape the resolved value, but adds no format or defaulting detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find design tokens by intent or name') with three concrete example queries, which makes the search semantics unambiguous. It does not name a sibling to contrast with, so the boundary with find_token_for_value (reverse value lookup) or resolve_token is left implicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use it before writing any color, spacing, radius or font value' gives a clear trigger condition tied to the agent's task. There are no explicit exclusions or named alternatives for cases like exact-name resolution, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.1.0- First observed
arbitrate_semver - First observed
audit_css - First observed
find_token_for_value - First observed
lint_naming - First observed
propose_token - First observed
resolve_token - First observed
search_tokens
TDQS
Scored across 7 tools
Each tool targets a distinct operation: intent/name search (search_tokens), single-token resolution (resolve_token), reverse lookup from a literal value (find_token_for_value), naming validation, CSS auditing, semver arbitration, and token proposal. The three token-lookup tools could superficially look similar, but their descriptions make the input types (intent phrase vs dot path vs literal value) unambiguous.
All seven tools follow a strict snake_case verb_noun pattern (search_tokens, resolve_token, find_token_for_value, lint_naming, audit_css, arbitrate_semver, propose_token). Verbs are meaningful and consistently chosen, with no camelCase or stylistic mixing.
Seven tools is well-scoped for a design-token governance server; each covers a distinct phase (discover, resolve, validate, audit, version, propose). No tool feels redundant or like padding.
The surface covers discovery, resolution, reverse lookup, validation, auditing, semver, and additive proposals well. Gaps remain around modifying or deleting existing tokens (rename/remove are only analyzed, not executed), but agents can work around this via propose_token and arbitrate_semver.
Maintenance
Related MCP Connectors
Give your agent a real design system: tokens, measured WCAG contrast, and rules to follow.
Access and maintain design system docs, tokens, components, skills, and contexts across any project.
Design system contracts, docs search, and usage validation for @digitaltableteur components.
BrandPilot brands and design systems for coding agents: tokens, design context, repo rules.
Related MCP Servers
- AlicenseAqualityAmaintenanceDesign contract layer for AI agents. Scans Figma, code, Storybook, and token files, reconciles conflicts, and serves a single machine-readable source of truth so every agent gets the same authoritative design rules before it builds. Local-first.969 npm21Apache 2.0
- AlicenseAqualityAmaintenanceEnables AI agents to score live URLs against a deterministic design contract, validate DTCG tokens and Lottie animations, audit accessibility, and retrieve design-system contracts, catalogs, and review rubrics.173 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to query a workspace's design system before writing UI and validate generated code against the same system afterward, using configurable token and component sources.4 npm1MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI coding agents to inspect a project's design tokens and component catalog, audit React/Tailwind files against seven static UI quality rules, score overall UI health, and apply auto-fixes that are re-audited and reverted on regression. It works both locally over filesystem access and remotely on raw TSX/JSX or CSS snippets passed directly as strings.-