get_theme_info
Retrieve design system theme details like colors, spacing, typography, and breakpoints. Use to extract CSS custom properties and streamline design system adoption or refactoring.
Instructions
Get design system theme information (colors, spacing, typography, breakpoints)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| includeAll | No | Whether to include all CSS custom properties found (default: false) |
Implementation Reference
- src/tools/get-theme-info.ts:22-179 (handler)The main execution function for the 'get_theme_info' tool. It validates input, fetches Storybook stories, identifies theme-related stories, extracts CSS custom properties from styles and inline styles, categorizes them into theme sections like colors, spacing, typography, etc., using helper functions like isColorValue and isSpacingValue, and returns formatted theme info.export async function handleGetThemeInfo(input: any) { try { const validatedInput = validateGetThemeInfoInput(input); const includeAll = validatedInput.includeAll || false; const client = new StorybookClient(); // Try to find a theme or foundation story const index = await client.fetchStoriesIndex(); const stories = index.stories || index.entries; if (!stories) { throw new Error('No stories found in Storybook index'); } // Look for theme-related stories const themePatterns = [ /theme/i, /design.*tokens/i, /foundation/i, /colors?/i, /typography/i, /spacing/i, /palette/i, ]; let themeStoryId: string | null = null; for (const [storyId, story] of Object.entries(stories)) { const title = story.title || ''; const name = story.name || story.story || ''; if (themePatterns.some(pattern => pattern.test(title) || pattern.test(name))) { themeStoryId = storyId; break; } } // If no theme story found, try to get any story to extract theme info if (!themeStoryId) { const firstStoryKey = Object.keys(stories)[0]; themeStoryId = firstStoryKey || null; } if (!themeStoryId) { throw new Error('No stories available to extract theme information'); } // Fetch the HTML and styles const componentHTML = await client.fetchComponentHTML(themeStoryId); // Extract all CSS custom properties const allTokens: Record<string, string> = {}; // Extract from styles if (componentHTML.styles) { for (const style of componentHTML.styles) { const tokens = extractDesignTokens(style); tokens.forEach(token => { allTokens[token.name] = token.value; }); } } // Extract from inline styles in HTML const styleRegex = /style="([^"]*)"/g; let match; while ((match = styleRegex.exec(componentHTML.html)) !== null) { if (match[1]) { const inlineStyle = match[1]; const tokens = extractDesignTokens(inlineStyle); tokens.forEach(token => { allTokens[token.name] = token.value; }); } } // Organize tokens into theme categories const theme: ThemeInfo = { colors: {}, spacing: {}, typography: {}, breakpoints: {}, shadows: {}, radii: {}, }; // Categorize tokens for (const [name, value] of Object.entries(allTokens)) { const lowerName = name.toLowerCase(); if ( lowerName.includes('color') || lowerName.includes('bg') || lowerName.includes('text') || lowerName.includes('border') || isColorValue(value) ) { theme.colors[name] = value; } else if ( lowerName.includes('space') || lowerName.includes('spacing') || lowerName.includes('margin') || lowerName.includes('padding') || lowerName.includes('gap') || isSpacingValue(value) ) { theme.spacing[name] = value; } else if ( lowerName.includes('font') || lowerName.includes('text') || lowerName.includes('line') || lowerName.includes('letter') ) { theme.typography[name] = value; } else if ( lowerName.includes('breakpoint') || lowerName.includes('screen') || lowerName.includes('media') ) { theme.breakpoints[name] = value; } else if (lowerName.includes('shadow') || lowerName.includes('elevation')) { theme.shadows[name] = value; } else if (lowerName.includes('radius') || lowerName.includes('rounded')) { theme.radii[name] = value; } else if (includeAll) { // If includeAll is true, add uncategorized tokens to a special category if (!(theme as any).other) { (theme as any).other = {}; } (theme as any).other[name] = value; } } // Add common breakpoint values if not found if (Object.keys(theme.breakpoints).length === 0) { theme.breakpoints = { '--breakpoint-xs': '0px', '--breakpoint-sm': '600px', '--breakpoint-md': '960px', '--breakpoint-lg': '1280px', '--breakpoint-xl': '1920px', }; } const response = { theme, totalTokens: Object.keys(allTokens).length, sourceStoryId: themeStoryId, }; return formatSuccessResponse( response, `Extracted ${Object.keys(allTokens).length} design tokens` ); } catch (error) { return handleError(error); } }
- src/tools/get-theme-info.ts:8-20 (schema)The Tool object definition for 'get_theme_info', including the name, description, and inputSchema used by the MCP server for tool listing and validation.export const getThemeInfoTool: Tool = { name: 'get_theme_info', description: 'Get design system theme information (colors, spacing, typography, breakpoints)', inputSchema: { type: 'object', properties: { includeAll: { type: 'boolean', description: 'Whether to include all CSS custom properties found (default: false)', }, }, }, };
- src/index.ts:15-24 (registration)Registration of the tool handler 'handleGetThemeInfo' mapped to 'get_theme_info' in the server's toolHandlers Map, used for handling CallTool requests.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:27-35 (registration)Registration of the getThemeInfoTool in the allTools array, used by the server to list available tools in response to ListTools requests.tools.listComponentsTool, tools.getComponentHTMLTool, tools.getComponentVariantsTool, tools.searchComponentsTool, tools.getComponentDependenciesTool, tools.getThemeInfoTool, tools.getComponentByPurposeTool, tools.getExternalCSSTool, ];
- src/utils/validators.ts:49-135 (schema)Zod schema and validation function for GetThemeInfoInput, used internally in the handler to validate and parse input.const GetThemeInfoInputSchema = z.object({ includeAll: z.boolean().optional(), }); const GetComponentByPurposeInputSchema = z.object({ purpose: z.string(), page: z.number().int().positive().optional(), pageSize: z.number().int().min(1).max(100).optional(), }); const GetComponentCompositionExamplesInputSchema = z.object({ componentId: z.string(), limit: z.number().optional(), }); export function validateListComponentsInput(input: any): ListComponentsInput { const parsed = ListComponentsInputSchema.parse(input); const result: ListComponentsInput = {}; if (parsed.category !== undefined) { result.category = parsed.category; } if (parsed.page !== undefined) { result.page = parsed.page; } if (parsed.pageSize !== undefined) { result.pageSize = parsed.pageSize; } return result; } export function validateGetComponentHTMLInput(input: any): GetComponentHTMLInput { const parsed = GetComponentHTMLInputSchema.parse(input); const result: GetComponentHTMLInput = { componentId: parsed.componentId, }; if (parsed.includeStyles !== undefined) { result.includeStyles = parsed.includeStyles; } return result; } export function validateGetComponentVariantsInput(input: any): GetComponentVariantsInput { return GetComponentVariantsInputSchema.parse(input); } export function validateSearchComponentsInput(input: any): SearchComponentsInput { const parsed = SearchComponentsInputSchema.parse(input); const result: SearchComponentsInput = { query: parsed.query, }; if (parsed.searchIn !== undefined) { result.searchIn = parsed.searchIn; } if (parsed.page !== undefined) { result.page = parsed.page; } if (parsed.pageSize !== undefined) { result.pageSize = parsed.pageSize; } return result; } export function validateGetComponentPropsInput(input: any): GetComponentPropsInput { return GetComponentPropsInputSchema.parse(input); } export function validateGetComponentDependenciesInput(input: any): GetComponentDependenciesInput { return GetComponentDependenciesInputSchema.parse(input); } export function validateGetLayoutComponentsInput(input: any): GetLayoutComponentsInput { const parsed = GetLayoutComponentsInputSchema.parse(input); const result: GetLayoutComponentsInput = {}; if (parsed.includeExamples !== undefined) { result.includeExamples = parsed.includeExamples; } return result; } export function validateGetThemeInfoInput(input: any): GetThemeInfoInput { const parsed = GetThemeInfoInputSchema.parse(input); const result: GetThemeInfoInput = {}; if (parsed.includeAll !== undefined) { result.includeAll = parsed.includeAll; } return result; }