list_components
Retrieve a detailed list of UI components (e.g., buttons, modals, cards) from your design system or Storybook. Filter by category, use pagination for large libraries, and explore available components for UI feature development.
Instructions
List all UI components available in your design system/Storybook. Returns components like modals, dialogs, buttons, forms, cards, etc. with their names, categories, and stories. Use this to explore what components are available for building UI features. Use category="all" or omit category parameter to list all components. Supports pagination to handle large component libraries.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | Filter components by category path (e.g., "Components/Buttons", "Layout"). Use "all" or omit to list all components. | |
| page | No | Page number (1-based). Default is 1. | |
| pageSize | No | Number of components per page (1-100). Default is 50. |
Input Schema (JSON Schema)
Implementation Reference
- src/tools/list-components.ts:33-93 (handler)The main handler function that implements the 'list_components' tool logic: validates input, fetches Storybook stories index, maps stories to components with optional category filter, applies pagination, and returns formatted response or error.export async function handleListComponents(input: any) { let validatedInput: any; try { validatedInput = validateListComponentsInput(input); const client = new StorybookClient(); const storiesIndex = await client.fetchStoriesIndex(); if (!storiesIndex) { throw new Error( 'Failed to fetch stories index - received null or undefined. Ensure Storybook is running and accessible at: ' + client.getStorybookUrl() ); } // Support both v3 (stories) and v4 (entries) format const storiesData = storiesIndex.stories || storiesIndex.entries; if (!storiesData || typeof storiesData !== 'object') { throw new Error( `Invalid stories index structure. Expected object with 'stories' or 'entries' property, got: ${JSON.stringify(storiesIndex).substring(0, 200)}... Check if your Storybook instance is properly configured.` ); } const stories = Object.values(storiesData); if (stories.length === 0) { return formatSuccessResponse( [], `No components found in Storybook at ${client.getStorybookUrl()}. Ensure your Storybook has stories configured and is accessible. Debug info: storiesData keys: ${Object.keys(storiesData).slice(0, 5).join(', ')}` ); } const filterFn = validatedInput.category && validatedInput.category !== 'all' ? (_story: any, _componentName: string, category?: string) => category === validatedInput.category : undefined; const componentMap = mapStoriesToComponents(storiesData, filterFn ? { filterFn } : {}); const allComponents = getComponentsArray(componentMap); // Apply pagination const paginationResult = applyPagination(allComponents, { page: validatedInput.page, pageSize: validatedInput.pageSize, }); const message = formatPaginationMessage( paginationResult, 'Found', `filter: ${validatedInput.category || 'none'}` ); return formatSuccessResponse(paginationResult.items, message); } catch (error) { return handleErrorWithContext(error, 'list components', { resource: 'components list', }); } }
- src/index.ts:26-35 (registration)Registers the listComponentsTool in the allTools array, which is returned in response to ListToolsRequest to expose available tools.const allTools = [ tools.listComponentsTool, tools.getComponentHTMLTool, tools.getComponentVariantsTool, tools.searchComponentsTool, tools.getComponentDependenciesTool, tools.getThemeInfoTool, tools.getComponentByPurposeTool, tools.getExternalCSSTool, ];
- src/index.ts:15-24 (registration)Registers the handler for 'list_components' tool in the toolHandlers Map, used by the server to dispatch CallToolRequests 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/tools/list-components.ts:8-31 (registration)Defines and exports the Tool object for 'list_components', including name, description, and inputSchema, which is later registered in the server.export const listComponentsTool: Tool = { name: 'list_components', description: 'List all UI components available in your design system/Storybook. Returns components like modals, dialogs, buttons, forms, cards, etc. with their names, categories, and stories. Use this to explore what components are available for building UI features. Use category="all" or omit category parameter to list all components. Supports pagination to handle large component libraries.', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter components by category path (e.g., "Components/Buttons", "Layout"). Use "all" or omit to list all components.', }, page: { type: 'number', description: 'Page number (1-based). Default is 1.', }, pageSize: { type: 'number', description: 'Number of components per page (1-100). Default is 50.', }, }, required: [], }, };
- src/utils/validators.ts:15-77 (schema)Defines Zod schema and validation function for ListComponentsInput, used in the handler to validate and parse tool inputs.const ListComponentsInputSchema = z.object({ category: z.string().optional(), page: z.number().int().positive().optional(), pageSize: z.number().int().min(1).max(100).optional(), }); const GetComponentHTMLInputSchema = z.object({ componentId: z.string(), includeStyles: z.boolean().optional(), }); const GetComponentVariantsInputSchema = z.object({ componentName: z.string(), }); const SearchComponentsInputSchema = z.object({ query: z.string(), searchIn: z.enum(['name', 'title', 'category', 'all']).optional(), page: z.number().int().positive().optional(), pageSize: z.number().int().min(1).max(100).optional(), }); const GetComponentPropsInputSchema = z.object({ componentId: z.string(), }); const GetComponentDependenciesInputSchema = z.object({ componentId: z.string(), }); const GetLayoutComponentsInputSchema = z.object({ includeExamples: z.boolean().optional(), }); 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; }