Skip to main content
Glama
freema

MCP Design System Extractor

get_external_css

Extract design tokens from external CSS files to analyze design systems without hitting response size limits. Returns colors, spacing, typography tokens and file statistics by default.

Instructions

Extract design tokens from CSS files. DEFAULT BEHAVIOR: Returns ONLY tokens and file statistics (small response ~1-3K tokens). Does NOT return CSS content by default to avoid token limits. For full CSS content, explicitly set includeFullCSS=true and maxContentSize. Perfect for analyzing design system tokens without hitting response size limits.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cssUrlYesURL of CSS file to analyze. IMPORTANT: Tool returns only design tokens by default (not CSS content) to avoid response size limits.
extractTokensNoExtract design tokens (colors, spacing, typography, etc.). Default: true. This is the main purpose of this tool.
includeFullCSSNoOPTIONAL: Set to true ONLY if you need the actual CSS content. WARNING: Large CSS files may hit token limits. Default: false (recommended).
maxContentSizeNoOPTIONAL: Max CSS content characters when includeFullCSS=true. Default: 10000. Larger files will be truncated.

Implementation Reference

  • Main execution logic for the 'get_external_css' tool. Validates input, resolves URL, performs domain security check, fetches CSS with timeout, extracts design tokens using extractDesignTokens, analyzes CSS stats, and returns structured JSON response with optional full CSS content.
    export async function handleGetExternalCSS(input: any) {
      let validatedInput: any;
      try {
        validatedInput = validateGetExternalCSSInput(input);
        const client = new StorybookClient();
        const baseUrl = client.getStorybookUrl();
    
        // Make URL absolute if it's relative
        const absoluteUrl = await makeAbsoluteUrl(validatedInput.cssUrl, baseUrl);
    
        // Security check: Only allow URLs from the same domain as Storybook
        if (!isDomainAllowed(absoluteUrl, baseUrl)) {
          const securityError = createSecurityError(
            'fetch external CSS',
            absoluteUrl,
            'CSS URL domain is not allowed. Only URLs from the Storybook domain are permitted for security reasons.'
          );
          throw new Error(securityError.message);
        }
    
        // Fetch CSS content with timeout
        const timeout = getEnvironmentTimeout(OPERATION_TIMEOUTS.fetchExternalCSS);
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), timeout);
    
        let response;
        try {
          response = await fetch(absoluteUrl, {
            signal: controller.signal,
            headers: {
              Accept: 'text/css,*/*;q=0.1',
              'User-Agent': 'MCP-Design-System-Extractor/1.0',
            },
          });
        } catch (error: any) {
          if (error.name === 'AbortError') {
            const timeoutError = createTimeoutError(
              'fetch external CSS',
              timeout,
              absoluteUrl,
              'CSS file'
            );
            throw new Error(timeoutError.message);
          }
          const connectionError = createConnectionError('fetch external CSS', absoluteUrl, error);
          throw new Error(connectionError.message);
        } finally {
          clearTimeout(timeoutId);
        }
    
        if (!response.ok) {
          const connectionError = createConnectionError(
            'fetch external CSS',
            absoluteUrl,
            `HTTP ${response.status}: ${response.statusText}`,
            response.status
          );
          throw new Error(connectionError.message);
        }
    
        const cssContent = await response.text();
        const contentLength = cssContent.length;
    
        // Always perform CSS analysis
        const analysis = analyzeCSSContent(cssContent);
    
        // Extract design tokens if requested
        let tokens: DesignToken[] = [];
        let organizedTokens: OrganizedTokens | null = null;
    
        if (validatedInput.extractTokens) {
          tokens = extractDesignTokens(cssContent);
          organizedTokens = organizeTokens(tokens);
        }
    
        // Build optimized response (tokens-first approach)
        const result: any = {
          url: absoluteUrl,
          originalUrl: validatedInput.cssUrl,
          fileSize: contentLength,
          fileSizeFormatted: formatBytes(contentLength),
          analysis,
          tokensExtracted: validatedInput.extractTokens,
          tokenCount: tokens.length,
          tokens: organizedTokens,
        };
    
        // Only include CSS content if explicitly requested
        if (validatedInput.includeFullCSS) {
          const maxSize = validatedInput.maxContentSize || 10000;
    
          if (contentLength <= maxSize) {
            result.content = cssContent;
            result.truncated = false;
            result.displayedSize = contentLength;
          } else {
            result.content =
              cssContent.substring(0, maxSize) +
              '\n\n/* ... Content truncated due to size limit ... */';
            result.truncated = true;
            result.displayedSize = maxSize;
            result.truncationWarning = `Content truncated from ${formatBytes(contentLength)} to ${formatBytes(maxSize)}. Use maxContentSize parameter to adjust limit.`;
          }
        }
    
        const message = validatedInput.includeFullCSS
          ? `CSS content included from ${validatedInput.cssUrl} (${formatBytes(contentLength)})${
              result.truncated ? ' - TRUNCATED due to size' : ''
            }${validatedInput.extractTokens ? `, ${tokens.length} design tokens extracted` : ''}`
          : `SUCCESS: Extracted ${tokens.length} design tokens from ${validatedInput.cssUrl} (${formatBytes(contentLength)} file). CSS content NOT included (use includeFullCSS=true if needed).`;
    
        return formatSuccessResponse(result, message);
      } catch (error) {
        return handleErrorWithContext(error, 'get external CSS', {
          url: validatedInput?.cssUrl || 'unknown',
          resource: 'external CSS file',
        });
      }
    }
  • Tool definition including input schema for validation. Defines parameters like cssUrl (required), extractTokens, includeFullCSS, maxContentSize.
    export const getExternalCSSTool: Tool = {
      name: 'get_external_css',
      description:
        'Extract design tokens from CSS files. DEFAULT BEHAVIOR: Returns ONLY tokens and file statistics (small response ~1-3K tokens). Does NOT return CSS content by default to avoid token limits. For full CSS content, explicitly set includeFullCSS=true and maxContentSize. Perfect for analyzing design system tokens without hitting response size limits.',
      inputSchema: {
        type: 'object',
        properties: {
          cssUrl: {
            type: 'string',
            description:
              'URL of CSS file to analyze. IMPORTANT: Tool returns only design tokens by default (not CSS content) to avoid response size limits.',
          },
          extractTokens: {
            type: 'boolean',
            description:
              'Extract design tokens (colors, spacing, typography, etc.). Default: true. This is the main purpose of this tool.',
          },
          includeFullCSS: {
            type: 'boolean',
            description:
              'OPTIONAL: Set to true ONLY if you need the actual CSS content. WARNING: Large CSS files may hit token limits. Default: false (recommended).',
          },
          maxContentSize: {
            type: 'number',
            description:
              'OPTIONAL: Max CSS content characters when includeFullCSS=true. Default: 10000. Larger files will be truncated.',
          },
        },
        required: ['cssUrl'],
      },
    };
  • src/index.ts:15-24 (registration)
    Registers the handler function for 'get_external_css' in the toolHandlers Map, which is used by the MCP server to dispatch tool calls to the correct handler.
    const toolHandlers = new Map<string, (input: any) => Promise<any>>([
      ['list_components', tools.handleListComponents],
      ['get_component_html', tools.handleGetComponentHTML],
      ['get_component_variants', tools.handleGetComponentVariants],
      ['search_components', tools.handleSearchComponents],
      ['get_component_dependencies', tools.handleGetComponentDependencies],
      ['get_theme_info', tools.handleGetThemeInfo],
      ['get_component_by_purpose', tools.handleGetComponentByPurpose],
      ['get_external_css', tools.handleGetExternalCSS],
    ]);
  • src/index.ts:26-35 (registration)
    Adds the getExternalCSSTool to the allTools array returned by ListToolsRequestHandler for tool discovery.
    const allTools = [
      tools.listComponentsTool,
      tools.getComponentHTMLTool,
      tools.getComponentVariantsTool,
      tools.searchComponentsTool,
      tools.getComponentDependenciesTool,
      tools.getThemeInfoTool,
      tools.getComponentByPurposeTool,
      tools.getExternalCSSTool,
    ];
  • Runtime input validation function that coerces and validates parameters against the schema, applying defaults.
    function validateGetExternalCSSInput(input: any): GetExternalCSSInput {
      if (!input || typeof input !== 'object') {
        throw new Error('Input must be an object');
      }
    
      if (!input.cssUrl || typeof input.cssUrl !== 'string') {
        throw new Error('cssUrl must be a non-empty string');
      }
    
      return {
        cssUrl: input.cssUrl,
        extractTokens: input.extractTokens !== false, // Default to true
        includeFullCSS: input.includeFullCSS === true, // Default to false
        maxContentSize: input.maxContentSize || 10000, // Default to 10000 chars
      };
    }
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key traits: the default response size (~1-3K tokens), the avoidance of token limits by not returning CSS content by default, and the need to set parameters for full content. However, it lacks details on error handling, performance, or authentication requirements, which are minor gaps.

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

Conciseness5/5

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

The description is front-loaded with the core purpose, followed by behavioral details and usage tips in a logical flow. Every sentence adds value without redundancy, making it efficient and well-structured for quick comprehension by an AI agent.

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

Completeness4/5

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

Given the complexity of a 4-parameter tool with no annotations and no output schema, the description is mostly complete. It covers purpose, usage, and key behaviors, but lacks information on output format or error cases. Since there's no output schema, some gaps remain in explaining what the tool returns beyond size estimates.

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

Parameters4/5

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

The schema description coverage is 100%, so the baseline is 3. The description adds value by explaining the purpose of parameters beyond the schema: it clarifies that extractTokens is the main purpose, warns about token limits with includeFullCSS, and notes that maxContentSize applies only when includeFullCSS is true. This enhances understanding but doesn't fully detail all parameter interactions.

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?

The description clearly states the specific verb ('extract') and resource ('design tokens from CSS files'), distinguishing it from sibling tools that focus on components, themes, or dependencies. It explicitly mentions the main purpose of analyzing design system tokens, making it distinct from tools that might retrieve CSS content or component details.

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?

The description provides explicit guidance on when to use this tool (for analyzing design tokens without hitting response limits) and when not to use it (if full CSS content is needed, requiring specific parameter settings). It contrasts the default behavior with alternatives by explaining how to adjust parameters for different needs, offering clear usage context.

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

Install Server

Other Tools

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/freema/mcp-design-system-extractor'

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