Skip to content

Add enriched design-token MCP server for AI coding assistants (proposal) - #684

Draft
ronnyhummitzsch wants to merge 35 commits into
Sage:masterfrom
ronnyhummitzsch:feat/enriched-tokens-mcp
Draft

Add enriched design-token MCP server for AI coding assistants (proposal)#684
ronnyhummitzsch wants to merge 35 commits into
Sage:masterfrom
ronnyhummitzsch:feat/enriched-tokens-mcp

Conversation

@ronnyhummitzsch

Copy link
Copy Markdown

Proposal — for review, not yet merge-ready (Draft)

This branch proposes adding a Model Context Protocol (MCP) server to
@sage/design-tokens so AI coding assistants can query the design tokens
directly — with both light and dark values, the source $description
context, and the fully resolved alias/layer chain. Opened as a Draft so it
can be evaluated at your leisure; happy to split, reshape, or drop parts based
on feedback.

Try it in 30 seconds (no install)

Download docs/mcp-demo.html and open it in a browser.
It is fully self-contained (assets base64-embedded, no server, no network) —
GitHub won't render it inline, so grab the raw file. It walks through what the
MCP exposes and why.

What it does

A self-contained component under mcp/. The style-dictionary build emits an
enriched artefact (dist/mcp/tokens.json, light/dark merged); the server loads
it and serves four tools over stdio:

Tool Purpose
get_token(name, mode?) Look up a token by kebab-case name → value, type, layer, category, reference, refChain, description. mode reduces mode-dependent fields to one mode's value.
search_tokens(query, category?, layer?, limit?) Multi-word substring search over token names, with optional category / layer filters.
list_categories() All categories with token counts.
list_tokens_by_category(category, limit?) All tokens in a category, including description where available.

What makes it more than a flat dump (current snapshot — see
mcp/REPORT.md): 1532 tokens, 1009 mode-divergent
(carry {light, dark}), 425 (27.7%) with a source description, and every
alias resolved into an explicit refChain (depth up to 4+).

Run the MCP server

npm install && npm run build      # produces dist/mcp/tokens.json (≈ 675 KB)
(cd mcp && npm install)           # MCP SDK for the server runtime

Point an MCP client at mcp/server.js. Full wiring (incl. the Claude Code
mcpServers entry) and the data shape are in mcp/README.md.

What's in this branch (the rigor)

  • Design specs + implementation plans under docs/superpowers/ — the
    reasoning behind the enriched build format and the hardening pass.
  • 81 tests, all green, including five hardening classes under
    tests/hardening/: data integrity (schema + every alias terminates at a
    literal + values match dist/css), adversarial input, realistic agent
    scenarios, a real MCP wire-protocol E2E (server spawned as subprocess, driven
    via the SDK Client), and self-containment (no host-absolute paths, etc.).
  • The detailed commit history is intentionally preserved (spec → plan →
    implementation → hardening → review feedback) rather than squashed.

Test plan

  • npm install && npm run build produces dist/mcp/tokens.json
  • npm test → 81 passing
  • (cd mcp && npm install), wire mcp/server.js into a client, call
    list_categories → returns core, global, mode + every component category
  • Open docs/mcp-demo.html and sanity-check the walkthrough
  • Optional: scripts/verify-fresh-clone.sh for a hands-off fresh-clone check

Open questions for review

  1. Does the MCP belong in this repo at all, or as a separate package that
    consumes the build artefact? (Phase 2 idea below.)
  2. Build artefact: is shipping dist/mcp/tokens.json the right contract, or
    should the enriched format be published with the npm package?
  3. Scope of the 4 tools — anything missing / anything redundant for how your
    assistants actually query tokens?
  4. Is the committed docs/mcp-demo.html useful to keep in-repo, or better as an
    out-of-repo artefact?

Scope / roadmap

Phase 2 (tracked separately): contribute the enriched build format upstream so
the MCP can ship with the package and downstream consumers use it without
cloning the repo.

ronnyhummitzsch and others added 30 commits May 27, 2026 22:41
Phase 1 design for extending sage-tokens-mcp with light/dark values,
$description context and resolved alias/layer chains, sourced from
data/tokens via a new enriched style-dictionary build output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task-by-task TDD plan: new custom/json-enriched format, per-mode build
output, light/dark postbuild merge, rewritten mcp/server.js, and the
.claude.json re-point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… dist/mcp/tokens.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…i-word search

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ption, comment)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Four test classes (data integrity vs dist/css, adversarial input,
end-to-end over MCP protocol, agent scenarios), a report generator,
and mcp/README.md. All hardening runs in npm test; npm run mcp:report
produces a Markdown snapshot for PR artefacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hardening class

Phase-1 spec is reframed away from "extension of an existing MCP" — the
MCP in mcp/ is a standalone component in this repo, replacing an earlier
external wrapper prototype. Tool table no longer compares to prior state.

Hardening spec gains test class 5: repository self-containment.
Asserts no host-absolute paths, no legacy wrapper references in
versioned code, presence of mcp/README.md with required sections, root
README mentions the MCP, and dist/mcp/tokens.json is reproducible
without secrets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Project policy: all committed artefacts (specs, plans, code comments,
documentation, reports) are written in English regardless of conversation
language. German remains the working-conversation language only.

Translates 2026-05-27 enriched MCP spec (143 lines) and implementation
plan (~750 lines), plus three lingering German inline comments inside the
plan's code samples.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ten tasks: five hardening test classes (data integrity, adversarial
input, agent scenarios, MCP wire-protocol E2E, repository
self-containment), mcp/README.md with the consumer flow, a root README
pointer, an npm-run-mcp:report Markdown snapshot, a fresh-clone
verification helper, and final acceptance verification against the
user's criteria (executable, executed, traceable, robust, ready for
daily use).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sation

Adds the data integrity hardening test class (schema invariants,
refChain termination, completeness vs dist/css, value consistency
for resolved layers).

The CSS-vs-MCP value consistency check exposed a real build bug:
postbuild.ts:fixShadowValues normalises bare numeric components in
shadow shorthand to px for CSS/SCSS outputs only. The MCP merged
output kept the source's unitless numerics ("0 0 1 1"), diverging
from what Sage ships in CSS ("0 0 1px 1px").

Fix applies the same normalisation to shadow tokens during the
light/dark merge in scripts/utils/merge-mcp-tokens.ts, so MCP values
match the CSS contract for the 7 affected global-depth tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The self-containment test reads tracked files via git ls-files and scans
for /Users/ paths and legacy wrapper references. After the test file
itself became tracked, the scan matched its own search patterns (the
test names and the literal it searches for). Test files legitimately
contain such strings as fixtures and patterns and are not production
code, so we exclude them from the scope alongside docs/superpowers/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Under @tsconfig/strictest, bins[key]++ failed because indexed access
yields number | undefined. Switched to bins[key] = (bins[key] ?? 0) + 1
mirroring the countBy pattern in the same file. Behaviour unchanged.
E2E error-contract test no longer tautological — it explicitly asserts
"throw OR isError:true" rather than collapsing both outcomes into a
trivially-true expression. Mode-reduction E2E test now picks a
genuinely mode-divergent token via list-then-filter and verifies the
reduced value equals the requested mode's side.

Data-integrity refChain termination checks both light AND dark
branches' termini for mode-divergent chains (previously only dark
was existence-checked).

mcp/tools.js: getToken null/undefined guard is now explicit (was
relying on data coincidence). searchTokens and listTokensByCategory
clamp negative limit to 0 to keep results.length and truncated
internally consistent.

verify-fresh-clone.sh removes the stale dist/mcp/tokens.json before
the build so the post-build existence check cannot pass against a
prior run's artefact.

Removes the unused kebabToDotted helper from data-integrity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ronnyhummitzsch and others added 5 commits May 28, 2026 11:35
A single-file, edge-to-edge slide presentation about the Sage Design
Tokens MCP. Uses the real Sage UI font weights (base64-embedded), the
authentic Sage token palette, and the live numbers from
dist/mcp/tokens.json. Ten editorial slides cover the motivation, the
architecture, the enriched token anatomy, the four MCP tools, the live
snapshot, the hardening, the quickstart, the roadmap, and a colophon.

Editorial / refined-minimalist aesthetic: Sage UI dominant, signature
green used as a marker rather than a fill, dot-grid background evokes a
design-system spec sheet, numbers ARE the design on the snapshot slide.
Light and dark themes both via real Sage tokens; press T to toggle live.

Navigation: ←/→ to navigate, Space toggles the index grid, Esc closes,
T toggles theme, R restarts, Home/End jump to extremes.

Build artefacts:
- scripts/build-mcp-demo.ts — generator: reads fonts + dist/mcp/tokens.json
  + git metadata, templates the HTML.
- package.json — npm run build:demo runs the generator.
- docs/mcp-demo.html — generated single-file output, ~157 KB,
  no external requests (inline favicon, base64 fonts, inline CSS/JS).

Verified at 1440×900 and 768×1024 via playwright. All ten slides render
correctly in both light and dark themes; index overlay grid works; the
self-containment story holds (no network calls, no external assets).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…reen

Acts on user feedback:
- Cover and end slides now use a 2/3 + 1/3 editorial grid. The right
  column carries an aside with edition copy and a live-stat readout,
  letting the title breathe and wrap naturally instead of crammed
  against the left rule.
- Italics removed from every headline accent. Sage brand has no
  italic, so emphasised words now use signature green at weight 700
  without any font-style:italic. Body em still upright + weight 500.
- Slide counter moved from the top-left (where it collided with the
  per-slide chapter eyebrow) into a new bottom-right navbar that pairs
  it with a discrete keyboard hint ("← → navigate · Space index ·
  F full"). The hint fades out when the index overlay is open.
- Fullscreen via the F key OR a Full icon button next to the theme
  toggle. The button label flips between "Full" / "Exit" via the
  fullscreenchange listener.
- Topbar no longer contains the counter — only theme + fullscreen
  controls live there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…slide

The five test classes already make the hardening story; advertising the
specific bugs that were caught during development isn't what the deck
should foreground. Title softened to "Five test classes. End to end."
and the .caught CSS removed since it's no longer used.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The slide-head parent already caps at 92ch, so the extra 60ch limit on
.lede only forced senseless line breaks where space was available — the
Anatomy slide's "A real entry from dist/mcp/tokens.json —
button-typical-primary-bg-default." wrapped before the token name fit.
Adds text-wrap: pretty so longer paragraphs (Why slide) still wrap
gracefully without an arbitrary character cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
scripts/build-mcp-bundle.ts assembles a self-contained MCP bundle under
../mcp-bundle/ for sharing with the team ahead of any upstream release.
Bundles: server.js (path-rewritten to read ./tokens.json), tools.js,
the live dist/mcp/tokens.json, REPORT.md, the standalone demo.html and
a README.md walking through setup. Run via `npm run build:bundle` and
ship the directory or an `npm pack`ed tarball.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@adrianbruntonsagecom

adrianbruntonsagecom commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

@ronnyhummitzsch - we're wanting to develop an MCP server also for a different reason. What progress have you made towards getting this formally approved as my understanding is only MCP servers listed in our Sage registry are approved for use? We'll follow whatever worked for you 👍

Important: Only MCP servers listed in the Sage private registry are authorized for use.

@ronnyhummitzsch

Copy link
Copy Markdown
Author

@adrianbruntonsagecom ... not at all - I use it locally for my own purposes, and since it works quite well, I considered upstreaming - after a quick chat with the repo owners, we said they will have a look and see if that is somewhat useful.

This is where we stand - once usefulness is agreed, I am happy to go for the approval process.

What do you mean by MCP for a different reason?

@adrianbruntonsagecom

Copy link
Copy Markdown
Contributor

@adrianbruntonsagecom ... not at all - I use it locally for my own purposes, and since it works quite well, I considered upstreaming - after a quick chat with the repo owners, we said they will have a look and see if that is somewhat useful.

This is where we stand - once usefulness is agreed, I am happy to go for the approval process.

What do you mean by MCP for a different reason?

Our purpose would be entirely outside the scope of this repo. We have a developer interested in creating an MCP server for our component library, to allow consuming apps have copilot be more aware of the schema of the components available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants