Skip to main content
Glama
kenneives

design-token-bridge-mcp

extract_tokens_from_css

Extract design tokens by parsing CSS custom properties for use in cross-platform theme generation and WCAG contrast validation.

Instructions

Parse CSS custom properties (variables) and extract design tokens

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cssYesThe contents of a CSS file with custom properties

Implementation Reference

  • Main handler function that extracts design tokens from CSS custom properties. Parses CSS variables and categorizes them into colors, typography, spacing, radii, and elevation tokens.
    export function extractTokensFromCSS(cssContent: string): DesignTokens {
      const tokens: DesignTokens = {};
      const vars = parseCustomProperties(cssContent);
    
      if (vars.size === 0) {
        throw new Error("No CSS custom properties found in the provided CSS.");
      }
    
      const colors: Record<string, ColorToken> = {};
      const typography: Record<string, TypographyToken> = {};
      const spacing: Record<string, number> = {};
      const radii: Record<string, number> = {};
      const elevation: Record<string, ElevationToken> = {};
    
      for (const [name, value] of vars) {
        const tokenName = name
          .replace(/^--/, "")
          .replace(/^(color|clr)-/, "")
          .replace(/^(font|type|text)-/, "")
          .replace(/^(space|spacing)-/, "")
          .replace(/^(radius|radii|rounded)-/, "")
          .replace(/^(shadow|elevation)-/, "");
    
        // Detect category by prefix
        if (name.match(/^--(color|clr|bg|fg|text-color|border-color)/)) {
          const hex = colorToHex(value);
          if (hex) colors[tokenName] = { value: hex };
        } else if (name.match(/^--(font|type|text)/)) {
          const typo = parseTypographyValue(name, value);
          if (typo) {
            const key = tokenName.replace(/^(size|weight|family|height)-?/, "");
            if (key) {
              typography[key] = { ...typography[key], ...typo };
            }
          }
        } else if (name.match(/^--(space|spacing|gap|padding|margin)/)) {
          const px = parseToPixels(value);
          if (px !== null) spacing[tokenName] = px;
        } else if (name.match(/^--(radius|radii|rounded|border-radius)/)) {
          const px = parseToPixels(value);
          if (px !== null) radii[tokenName] = px;
        } else if (name.match(/^--(shadow|elevation)/)) {
          const parsed = parseShadowValue(value);
          if (parsed) elevation[tokenName] = parsed;
        } else {
          // Try to auto-detect by value format
          const hex = colorToHex(value);
          if (hex) {
            colors[tokenName] = { value: hex };
          } else {
            const px = parseToPixels(value);
            if (px !== null) spacing[tokenName] = px;
          }
        }
      }
    
      if (Object.keys(colors).length > 0) tokens.colors = colors;
      if (Object.keys(typography).length > 0) tokens.typography = typography;
      if (Object.keys(spacing).length > 0) tokens.spacing = spacing;
      if (Object.keys(radii).length > 0) tokens.radii = radii;
      if (Object.keys(elevation).length > 0) tokens.elevation = elevation;
    
      const hasContent = Object.values(tokens).some((v) => v !== undefined);
      if (!hasContent) {
        throw new Error("CSS custom properties found but none could be mapped to design tokens.");
      }
    
      return tokens;
    }
  • src/index.ts:56-73 (registration)
    Tool registration with MCP server. Defines the tool name 'extract_tokens_from_css', input schema (CSS string), and async handler that calls extractTokensFromCSS.
    server.registerTool(
      "extract_tokens_from_css",
      {
        description:
          "Parse CSS custom properties (variables) and extract design tokens",
        inputSchema: {
          css: z.string().describe("The contents of a CSS file with custom properties"),
        },
      },
      async ({ css }) => {
        try {
          const tokens = extractTokensFromCSS(css);
          return toolResult(JSON.stringify(tokens, null, 2));
        } catch (e) {
          return errorResult(e);
        }
      }
    );
  • DesignTokens interface defining the output schema with optional colors, typography, spacing, radii, elevation, and motion token collections.
    export interface DesignTokens {
      colors?: Record<string, ColorToken>;
      typography?: Record<string, TypographyToken>;
      spacing?: Record<string, number>; // px values
      radii?: Record<string, number>; // px values
      elevation?: Record<string, ElevationToken>;
      motion?: Record<string, MotionToken>;
    }
  • Helper function parseCustomProperties that extracts CSS custom properties from the document using regex, handling comments and avoiding duplicate declarations.
    function parseCustomProperties(css: string): Map<string, string> {
      const vars = new Map<string, string>();
    
      // Remove comments
      const cleaned = css.replace(/\/\*[\s\S]*?\*\//g, "");
    
      // Match property declarations: --name: value;
      const propRegex = /(--[\w-]+)\s*:\s*([^;]+);/g;
      let match;
      while ((match = propRegex.exec(cleaned)) !== null) {
        const name = match[1].trim();
        const value = match[2].trim();
        // Don't overwrite — first declaration wins (light mode / :root)
        if (!vars.has(name)) {
          vars.set(name, value);
        }
      }
    
      return vars;
    }
  • Helper function colorToHex that converts various CSS color formats (hex, rgb, rgba, hsl, hsla) to normalized hex format for token output.
    function colorToHex(value: string): string | null {
      // Already hex
      if (/^#[0-9a-fA-F]{3,8}$/.test(value)) {
        return normalizeHex(value);
      }
    
      // rgb(r, g, b) or rgba(r, g, b, a)
      const rgbMatch = value.match(
        /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*[\d.]+)?\s*\)/
      );
      if (rgbMatch) {
        const r = parseInt(rgbMatch[1]).toString(16).padStart(2, "0");
        const g = parseInt(rgbMatch[2]).toString(16).padStart(2, "0");
        const b = parseInt(rgbMatch[3]).toString(16).padStart(2, "0");
        return `#${r}${g}${b}`.toUpperCase();
      }
    
      // hsl(h, s%, l%) — approximate conversion
      const hslMatch = value.match(
        /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%/
      );
      if (hslMatch) {
        return hslToHex(
          parseFloat(hslMatch[1]),
          parseFloat(hslMatch[2]),
          parseFloat(hslMatch[3])
        );
      }
    
      return null;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kenneives/design-token-bridge-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server