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-server what 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.
Inputs: changes (required). Returns: bump, changes, warnings, principle
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.
Inputs: css (required), mode, allow, brand. Returns: ok, findings, errorCount, warningCount
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.
Inputs: value (required), mode, type, brand, +2 more. Returns: type, exact, value, nearest, +2 more
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.
Inputs: names (required). Returns: valid, results
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.
Inputs: path (required), type (required), value (required), mode, +3 more. Returns: diff, path, patch, status, +4 more
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.
Inputs: mode (required), path (required), brand (required). Returns: mode, path, type, brand, +9 more
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.
Inputs: query (required), mode, brand, limit. Returns: query, total, results
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
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.
Related MCP Connectors
Pre-execution governance for AI agents. Deterministic PASS/FAIL/REVIEW verdicts, replayable proof.
Pre-trade token safety check for AI agents. Simulates a sell before you buy, then returns one low/medium/high/unknown verdict with the signals behind it: sellability, buy/sell tax, liquidity depth, pair age, same-ticker impersonation, owner powers from bytecode. Ethereum, BSC, Base, Solana. Fail-closed - a check that cannot run answers unknown, never low. Publishes its own measured error rate with the benchmark harness in the repo. Free, no signup, no API key, MIT.
Query your team's drift, vulnerability, and upgrade data from any AI assistant. OAuth 2.1, 51 tools.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Related MCP Servers
- AlicenseAqualityBmaintenanceDesign 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.937 npm21Apache 2.0
- AlicenseAqualityBmaintenanceEnables 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.175 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.13 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.-