Skip to main content
Glama
marvkr

Better Design

Extract Design System From Existing Code

extract-design-system
Read-onlyIdempotent

Reads a project's existing CSS, tokens, or live-site styles and distills its design tokens so new components match the app's current look.

Instructions

Distill the design tokens out of a project's EXISTING CSS so new components match the app the user already built.

Use this when the user says "match our existing style", "use our design system", "style this like the rest of the app", or you are adding UI to a codebase that already has its own look. This is the inverse of find-design-system / create-design-system: instead of giving the user one of our systems, it reads theirs.

How to use it: read the project's globals.css (or wherever the design tokens / CSS custom properties live, e.g. a Tailwind v4 @theme block) and pass its full contents as css. The MCP server cannot read your filesystem, so you must paste the text.

Returns: the semantic palette (primary, background, foreground, accent, border, ring, etc.), radius, fonts, and shadow scale, plus a pasteable token sheet with every color normalized to oklch(). Handles hex, rgb()/rgba(), hsl()/hsla(), named colors, bare Shopify "r,g,b" triplets, and existing oklch values, across :root, .dark, and @theme blocks.

Beyond pasted CSS, sources accepts any mix of inputs and merges them (later sources win):

  • kind "css": any stylesheet text, same as the css param.

  • kind "computed": a JSON sample of rendered styles read off the LIVE site, which beats variable-name guessing because it records what elements actually look like. Gather it by running this in the site's browser console and passing the printed JSON:

JSON.stringify((() => {
  const pick = (el, props) => {
    if (!el) return null;
    const cs = getComputedStyle(el);
    return Object.fromEntries(props.map((p) => [p, cs[p]]));
  };
  const clear = (c) => {
    const flat = (c || "").split(" ").join("");
    return !flat || flat === "transparent" || flat.endsWith(",0)") || flat.endsWith("/0)");
  };
  const aside = "dialog,[role=dialog],[aria-modal=true],[class*=cookie],[id*=cookie],[class*=consent],[id*=consent],[class*=cky-]";
  const action = (selector, props) => {
    const all = Array.from(document.querySelectorAll(selector)).slice(0, 200).filter((el) => !el.closest(aside));
    const shown = all.filter((el) => {
      const box = el.getBoundingClientRect();
      return box.width > 0 && box.height > 0 && !clear(getComputedStyle(el).backgroundColor);
    });
    const isPrimaryClass = (t) => t === "primary" || ((t.startsWith("btn") || t.startsWith("button")) && t.endsWith("primary"));
    const named = shown.find((el) => String(el.className).split(" ").some(isPrimaryClass));
    const spread = (el) => {
      const rgb = (getComputedStyle(el).backgroundColor.match(/[0-9.]+/g) || []).slice(0, 3).map(Number);
      return rgb.length === 3 ? Math.max(...rgb) - Math.min(...rgb) : 0;
    };
    const colourful = shown.slice(0, 12).reduce((best, el) => (spread(el) > (best ? spread(best) : 39) ? el : best), null);
    const sample = pick(named ?? colourful ?? shown[0] ?? all[0] ?? null, props);
    return sample && { ...sample, namedPrimary: named ? "yes" : "" };
  };
  return {
    body: pick(document.querySelector("body"), ["backgroundColor","color","fontFamily"]),
    heading: pick(document.querySelector("h1,h2"), ["fontFamily"]),
    link: pick(document.querySelector("a[href]"), ["color"]),
    button: action("button,[role=button],.btn,input[type=submit],a[class*=button],a[class*=btn]", ["backgroundColor","color","borderRadius"]),
    input: pick(document.querySelector("input:not([type=hidden]),textarea,select"), ["borderColor","borderRadius"]),
    card: pick(document.querySelector("[class*=card]"), ["backgroundColor","color","borderRadius","boxShadow"]),
  };
})())
  • kind "tokens": a design-tokens JSON export ($value entries, W3C format or the Figma variables API shape with 0..1 rgb floats).

Common vendor names (Shopify's --color-button family) are mapped into empty semantic slots automatically and the mapping is reported in the output.

Input Schema

NameRequiredDescriptionDefault
cssNoFull contents of the project's globals.css (or any CSS holding the design tokens / CSS custom properties). Optional when `sources` is given.
nameNoOptional label for the extracted system, e.g. the app or repo name.
sourcesNoAny mix of brand sources, merged in order (later sources win). See the tool description for how to gather each kind.
maxTokensNoMaximum tokens to return. Truncates the token sheet to fit.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.1.0

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, but the description adds genuinely new operational context: the server cannot read the filesystem so CSS must be pasted, sources merge in order with later ones winning, vendor names are auto-mapped into empty semantic slots, and the exact return contents are enumerated. The output normalization rules (everything to oklch, support for hex/rgb/hsl/named/Shopify triplets, :root/.dark/@theme handling) go well beyond what the annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well front-loaded: purpose, then when-to-use, then how-to, then return shape, then source kinds. The embedded ~40-line browser script is long for a description field, but it is the only practical way to convey how to produce a valid "computed" source, so it earns its space. Minor cost only for length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by describing the returned artifacts (semantic palette, radius, fonts, shadow scale, pasteable oklch token sheet). With four optional params fully covered and a clear explanation of input gathering, an agent has everything needed to call this correctly without follow-up.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3, but the description earns more by explaining what each `sources.kind` actually is and how to obtain it — including a runnable console snippet for the "computed" kind that records live rendered styles. It also documents the merge precedence across up to 10 sources and the relationship between `css` and `sources` (css optional when sources given).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ("Distill the design tokens out of a project's EXISTING CSS") plus the motivation (new components match the existing app). It also explicitly positions itself as "the inverse of find-design-system / create-design-system," so the agent can separate it from the closest siblings without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete trigger utterances ("match our existing style", "use our design system") and the situational condition (adding UI to a codebase that already has its own look). It also names the alternatives it is not — find-design-system and create-design-system — and states what those do instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.