MCP server
A Model Context Protocol server, @unpunnyfuns/swatchbook-mcp exposes a DTCG project’s tokens, axes, and diagnostics to AI agents without running Storybook.
What it’s for
Section titled “What it’s for”Agents that need to reason about your design tokens (figma-to-token round-trips, alias-chain navigation, CI lint hooks, AI-assisted authoring) without spinning up a Storybook iframe. Point it at a swatchbook.config.{ts,mts,js,mjs} or a bare DTCG resolver.json and it parses the project on startup, then answers MCP tool calls against the resolved graph.
Install
Section titled “Install”npm install -D @unpunnyfuns/swatchbook-mcpOr run straight from npx; the package ships a swatchbook-mcp bin.
# full config (shared with the Storybook addon's `configPath`)npx @unpunnyfuns/swatchbook-mcp --config swatchbook.config.ts
# or a bare DTCG resolver — no wrapper needed, other options defaultnpx @unpunnyfuns/swatchbook-mcp --config tokens/resolver.jsonCLI flags:
| Flag | Description |
|---|---|
--config <path>, -c | Required. A swatchbook.config.{ts,mts,js,mjs} (full config) or a DTCG resolver.json (bare, other options at defaults). |
--cwd <path> | Override the working directory for resolving relative resolver / tokens paths. |
--no-watch | Disable live-reload. By default the server watches the config + resolved source files and swaps in fresh data on edits. |
--help, -h | Print usage and exit. |
Wire into an MCP client
Section titled “Wire into an MCP client”Add an entry to the client’s MCP server config:
{ "mcpServers": { "swatchbook": { "command": "npx", "args": [ "-y", "@unpunnyfuns/swatchbook-mcp", "--config", "/absolute/path/to/swatchbook.config.ts" ] } }}The mcpServers shape is the same across MCP hosts; consult the host’s docs for where its config file lives. Restart the client and ask it about your tokens; it’ll call describe_project first, then drill into whatever you asked about.
| Tool | Inputs | Returns |
|---|---|---|
describe_project | (none) | Orientation: counts, axes, themes, presets, diagnostic totals, $types present. |
list_tokens | filter? path glob, type? DTCG $type, theme? name | Array of { path, type?, value } from the named theme (or default). Use first to discover paths. |
search_tokens | query, theme?, limit? | Case-insensitive substring search across paths, descriptions, and values. Returns matches + matchedIn hint. |
resolve_theme | tuple?, filter?, type? | Resolved token map for an axis tuple ({ mode: "Dark", brand: "…" }). Fills omitted axes from defaults; omitting tuple entirely resolves the project default theme. |
get_token | path | Full detail: per-theme value, alias chain, aliased-by list, CSS var reference, $deprecated, and an axis-variance summary (when the value flips across axes). |
get_alias_chain | path | Forward alias chain per theme (path → ... → primitive). Empty when the token is a primitive. |
get_aliased_by | path, maxDepth? | Backward alias tree: every token that resolves through this path. Shallower aliases first; cycles in the alias graph are caught and broken. |
get_css_usage | path, tuple? | CSS var, resolved value, compound [data-…] selector + HTML attrs needed to pin the tuple on <html>. |
get_color_formats | path, theme? | Color token rendered in hex / rgb / hsl / oklch / raw, each with an outOfGamut flag. |
get_color_contrast | foreground, background, theme?, algorithm? | Pair-wise contrast between two color tokens. Returns the score under value: wcag21 → ratio (1–21) + AA/AAA pass flags (normal + large text); apca → signed Lc + body / large-text / non-text pass flags. |
get_axis_variance | path | Classify how the token’s resolved value depends on each axis. Returns kind (constant / single / multi), varying vs constant axes, and a per-axis breakdown with the value seen in each context. |
list_axes | (none) | Axes + contexts + themes + presets from the project config. |
get_diagnostics | severity? 'error' | 'warn' | 'info' | Parser / resolver / validation diagnostics. |
emit_css | (none) | Full project stylesheet: :root default + per-tuple compound-selector blocks. |
Path globs accept * (one segment), ** (any number of segments trailing or mid-path), or exact dot-paths.
Programmatic use
Section titled “Programmatic use”For embedding the server in a larger toolchain:
import { createServer, loadFromConfig } from '@unpunnyfuns/swatchbook-mcp';import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const { project } = await loadFromConfig('swatchbook.config.ts');const server = createServer(project);await server.connect(new StdioServerTransport());loadFromConfig accepts the same .ts / .mts / .js / .mjs / .json shapes the CLI does. createServer(project) returns the server augmented with setProject(next); call it after re-loading the project to swap in fresh data without restarting the MCP transport. The CLI uses this to live-reload on token edits.
See also
Section titled “See also”@unpunnyfuns/swatchbook-core: the loader this server wraps.- Model Context Protocol: upstream spec.
- MCP Inspector: web UI to poke the server interactively.