search_components
Search and filter design system components by name, title, or category to quickly locate UI elements like modals, dialogs, buttons, and forms. Supports pagination for large datasets and case-insensitive partial matching.
Instructions
Search design system components by name, title, or category. Find UI components like modals, dialogs, popups, overlays, buttons, forms, cards, etc. Name is the component name only (e.g., "Modal", "Dialog"), title is the full story path (e.g., "Components/Overlays/Modal"), category is the grouping (e.g., "Components/Overlays"). Use "*" as query to list all components. Supports pagination for large result sets.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Page number (1-based). Default is 1. | |
| pageSize | No | Number of components per page (1-100). Default is 50. | |
| query | Yes | The search query to match (e.g., "button", "form", "nav"). Use "*" to list all components. Case-insensitive partial matching. | |
| searchIn | No | Where to search: "name" (component name only), "title" (full path), "category" (grouping), or "all" (search everywhere, default) |
Input Schema (JSON Schema)
Implementation Reference
- src/tools/search-components.ts:39-99 (handler)The main handler function that executes the search_components tool logic: validates input, fetches Storybook stories, filters by query and searchIn, maps to components, applies pagination, and returns formatted results.export async function handleSearchComponents(input: any) { try { const validatedInput = validateSearchComponentsInput(input); const client = new StorybookClient(); const searchIn = validatedInput.searchIn || 'all'; const query = validatedInput.query.toLowerCase(); // Handle wildcard queries - if query is just "*" or empty, match everything const isWildcard = query === '*' || query === '' || query === '.*'; const storiesIndex = await client.fetchStoriesIndex(); const stories = storiesIndex.stories || storiesIndex.entries || {}; const filterFn = (story: any, componentName: string, _category?: string) => { const storyTitle = story.title || ''; const categoryParts = storyTitle.split('/').slice(0, -1); const storyCategory = categoryParts.length > 0 ? categoryParts.join('/') : undefined; if (isWildcard) { return true; } switch (searchIn) { case 'name': return componentName.toLowerCase().includes(query); case 'title': return storyTitle.toLowerCase().includes(query); case 'category': return storyCategory ? storyCategory.toLowerCase().includes(query) : false; case 'all': default: return ( componentName.toLowerCase().includes(query) || storyTitle.toLowerCase().includes(query) || Boolean(storyCategory?.toLowerCase().includes(query)) ); } }; const componentMap = mapStoriesToComponents(stories, { filterFn }); const allResults = getComponentsArray(componentMap); // Apply pagination const paginationResult = applyPagination(allResults, { page: validatedInput.page, pageSize: validatedInput.pageSize, }); const message = formatPaginationMessage( paginationResult, 'Found', `matching "${validatedInput.query}", searched in: ${searchIn}` ); return formatSuccessResponse(paginationResult.items, message); } catch (error) { return handleErrorWithContext(error, 'search components', { resource: 'component search results', }); } }
- src/tools/search-components.ts:8-37 (schema)Tool specification including name, description, and input schema for the search_components tool.export const searchComponentsTool: Tool = { name: 'search_components', description: 'Search design system components by name, title, or category. Find UI components like modals, dialogs, popups, overlays, buttons, forms, cards, etc. Name is the component name only (e.g., "Modal", "Dialog"), title is the full story path (e.g., "Components/Overlays/Modal"), category is the grouping (e.g., "Components/Overlays"). Use "*" as query to list all components. Supports pagination for large result sets.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'The search query to match (e.g., "button", "form", "nav"). Use "*" to list all components. Case-insensitive partial matching.', }, searchIn: { type: 'string', enum: ['name', 'title', 'category', 'all'], description: 'Where to search: "name" (component name only), "title" (full path), "category" (grouping), or "all" (search everywhere, default)', }, 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: ['query'], }, };
- src/index.ts:15-24 (registration)Registers the handler for 'search_components' in the toolHandlers Map used by the MCP server for 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:26-35 (registration)Includes searchComponentsTool in the allTools array returned by ListToolsRequest handler.const allTools = [ tools.listComponentsTool, tools.getComponentHTMLTool, tools.getComponentVariantsTool, tools.searchComponentsTool, tools.getComponentDependenciesTool, tools.getThemeInfoTool, tools.getComponentByPurposeTool, tools.getExternalCSSTool, ];
- src/utils/validators.ts:30-108 (schema)Zod schema definition and validation function for SearchComponentsInput used in the handler.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; } 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;