Skip to main content
Version: 0.13

MCP server

Published as @unpunnyfuns/swatchbook-mcp. Model Context Protocol server for swatchbook — exposes a DTCG project's tokens, axes, and diagnostics to AI agents without running Storybook.

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

npm install -D @unpunnyfuns/swatchbook-mcp

Or run straight from npx — the package ships a swatchbook-mcp bin.

Run

# 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 default
npx @unpunnyfuns/swatchbook-mcp --config tokens/resolver.json

CLI flags:

FlagWhat
--config <path>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-watchDisable live-reload. By default the server watches the config + resolved source files and swaps in fresh data on edits.
--help, -hPrint usage and exit.

Wire into an MCP client

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
"mcpServers": {
"swatchbook": {
"command": "npx",
"args": [
"-y",
"@unpunnyfuns/swatchbook-mcp",
"--config",
"/absolute/path/to/swatchbook.config.ts"
]
}
}
}

Restart the client and ask it about your tokens — it'll call describe_project first, then drill into whatever you asked about.

Tools

ToolInputsReturns
describe_project(none)Orientation: counts, axes, themes, presets, diagnostic totals, $types present.
list_tokensfilter? path glob, type? DTCG $type, theme? nameArray of { path, type?, value } from the named theme (or default). Use first to discover paths.
search_tokensquery, theme?, limit?Case-insensitive substring search across paths, descriptions, and values. Returns matches + matchedIn hint.
resolve_themetuple, filter?, type?Resolved token map for an axis tuple ({ mode: "Dark", brand: "…" }). Fills omitted axes from defaults.
get_tokenpathFull detail: per-theme value, alias chain, aliased-by list, CSS var reference.
get_alias_chainpathForward alias chain per theme (path → ... → primitive). Empty when the token is a primitive.
get_aliased_bypath, maxDepth?Backward alias tree — every token that resolves through this path. Breadth-first with cycle protection.
get_consumer_outputpath, tuple?CSS var, resolved value, compound [data-…] selector + HTML attrs needed to pin the tuple on <html>.
get_color_formatspath, theme?Color token rendered in hex / rgb / hsl / oklch / raw, each with an outOfGamut flag.
get_color_contrastforeground, background, theme?, algorithm?Pair-wise contrast between two color tokens. wcag21 → ratio + AA/AAA pass flags (normal + large text). apca → signed Lc + body / large-text / non-text pass flags.
get_axis_variancepathClassify 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_diagnosticsseverity? '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

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