Skip to main content
Glama
YonasValentin

Design Inspiration MCP Server

design_extract_tokens

Extract design tokens like colors, typography, and spacing from any live website URL to analyze and implement visual styles.

Instructions

Extract actual design tokens (colors, typography, spacing, borders, shadows) from a live website using headless browser. Give it any URL and get back the exact values used. Pairs well with the search tools — find inspiration, then extract tokens from sites you like.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesWebsite URL to extract design tokens from. Examples: "https://stripe.com", "https://linear.app"
dark_modeNoExtract colors from dark mode variant
mobileNoExtract from mobile viewport (375px)

Implementation Reference

  • The tool handler function for "design_extract_tokens" which orchestrates the call to `runDembrandt` and `formatTokens`.
    }, async (params: ExtractTokensInput) => {
      try {
        const url = params.url.startsWith("http") ? params.url : `https://${params.url}`;
        const flags: string[] = [];
        if (params.dark_mode) flags.push("--dark-mode");
        if (params.mobile) flags.push("--mobile");
    
        const stdout = await runDembrandt(url, flags);
        const tokens: DesignTokens = JSON.parse(stdout);
        const text = formatTokens(tokens, url);
    
        return {
          content: [{ type: "text" as const, text }],
          structuredContent: { url, dark_mode: params.dark_mode, mobile: params.mobile, tokens },
        };
      } catch (error) {
        return {
          content: [
            {
              type: "text" as const,
              text: error instanceof Error ? error.message : `Error: ${String(error)}`,
            },
          ],
        };
      }
    });
  • Zod schema defining the inputs for the design_extract_tokens tool.
    const ExtractTokensInputSchema = z
      .object({
        url: z
          .string()
          .min(4, "URL is required")
          .describe('Website URL to extract design tokens from. Examples: "https://stripe.com", "https://linear.app"'),
        dark_mode: z
          .boolean()
          .default(false)
          .describe("Extract colors from dark mode variant"),
        mobile: z
          .boolean()
          .default(false)
          .describe("Extract from mobile viewport (375px)"),
      })
      .strict();
  • src/index.ts:515-524 (registration)
    Tool registration for "design_extract_tokens" with the MCP server.
    server.registerTool("design_extract_tokens", {
      title: "Extract design tokens from website",
      description: `Extract actual design tokens (colors, typography, spacing, borders, shadows) from a live website using headless browser. Give it any URL and get back the exact values used. Pairs well with the search tools — find inspiration, then extract tokens from sites you like.`,
      inputSchema: ExtractTokensInputSchema,
      annotations: {
        readOnlyHint: true,
        destructiveHint: false,
        idempotentHint: true,
        openWorldHint: true,
      },
  • Helper function to execute the dembrandt command-line tool.
    function runDembrandt(url: string, flags: string[]): Promise<string> {
      return new Promise((resolve, reject) => {
        const args = [url, "--json-only", ...flags];
        execFile("dembrandt", args, { timeout: 60_000, maxBuffer: 5 * 1024 * 1024 }, (error, stdout, stderr) => {
          if (error) {
            const msg = stderr?.trim() || error.message;
            if (error.killed) return reject(new Error("Timed out after 60s. The site may be too slow — try with --slow via CLI."));
            if (msg.includes("net::ERR_NAME_NOT_RESOLVED")) return reject(new Error(`Could not resolve URL: ${url}`));
            if (msg.includes("net::ERR_CONNECTION_REFUSED")) return reject(new Error(`Connection refused: ${url}`));
            return reject(new Error(`dembrandt failed: ${msg}`));
          }
          resolve(stdout);
        });
      });
  • Helper function to format the design tokens into a Markdown representation.
    function formatTokens(tokens: DesignTokens, url: string): string {
      const lines = [`# Design Tokens: ${url}`, ""];
    
      if (tokens.colors && Object.keys(tokens.colors).length) {
        lines.push("## Colors", "");
        for (const [name, value] of Object.entries(tokens.colors)) {
          if (typeof value === "string") {
            lines.push(`- **${name}**: \`${value}\``);
          } else if (typeof value === "object" && value !== null) {
            lines.push(`- **${name}**: \`${JSON.stringify(value)}\``);
          }
        }
        lines.push("");
      }
    
      if (tokens.typography && Object.keys(tokens.typography).length) {
        lines.push("## Typography", "");
        for (const [name, value] of Object.entries(tokens.typography)) {
          if (typeof value === "string") {
            lines.push(`- **${name}**: \`${value}\``);
          } else if (typeof value === "object" && value !== null) {
            lines.push(`- **${name}**: \`${JSON.stringify(value)}\``);
          }
        }
        lines.push("");
      }
    
      if (tokens.spacing && Object.keys(tokens.spacing).length) {
        lines.push("## Spacing", "");
        for (const [name, value] of Object.entries(tokens.spacing)) {
          lines.push(`- **${name}**: \`${JSON.stringify(value)}\``);
        }
        lines.push("");
      }
    
      if (tokens.borders && Object.keys(tokens.borders).length) {
        lines.push("## Borders", "");
        for (const [name, value] of Object.entries(tokens.borders)) {
          lines.push(`- **${name}**: \`${JSON.stringify(value)}\``);
        }
        lines.push("");
      }
    
      if (tokens.shadows && Object.keys(tokens.shadows).length) {
        lines.push("## Shadows", "");
        for (const [name, value] of Object.entries(tokens.shadows)) {
          lines.push(`- **${name}**: \`${JSON.stringify(value)}\``);
        }
        lines.push("");
      }
    
      // Any remaining top-level keys
      const handled = new Set(["colors", "typography", "spacing", "borders", "shadows"]);
      for (const [key, value] of Object.entries(tokens)) {
        if (handled.has(key) || value === undefined || value === null) continue;
        lines.push(`## ${key.charAt(0).toUpperCase() + key.slice(1)}`, "");
        if (typeof value === "object") {
          for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
            lines.push(`- **${k}**: \`${typeof v === "string" ? v : JSON.stringify(v)}\``);
          }
        } else {
          lines.push(`- ${JSON.stringify(value)}`);
        }
        lines.push("");
      }
    
      let result = lines.join("\n");
      if (result.length > CHARACTER_LIMIT) {
        result = result.slice(0, CHARACTER_LIMIT) + "\n\n...(truncated)";
      }
      return result;
    }

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/YonasValentin/design-inspiration-mcp-server'

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