Skip to main content
Glama
by m-yoshiro
storybook-api.ts3.46 kB
import { promises as fs } from 'node:fs'; import path from 'path'; import { getAbsolutePath } from './utils.js'; // Storybook json types // https://github.com/storybookjs/storybook/blob/next/code/core/src/types/modules/indexer.ts export interface Parameters { __id: string; docsOnly: boolean; fileName: string; // biome-ignore lint/suspicious/noExplicitAny: we can't expect the exact structure of the parameters [name: string]: any; } export interface ComponentProp { name: string; type: string; // biome-ignore lint/suspicious/noExplicitAny: we can't expect the type of the defaultValue defaultValue: any; description?: string; } export interface StorybookStory { id: string; title: string; name: string; importPath: string; kind: string; story: string; parameters: Parameters; fullPath?: string; } export interface ComponentStory { name: string; parameters: Parameters; id?: string; title: string; importPath?: string; componentPath?: string; kind?: string; story?: string; storyFileFullPath?: string; componentFullPath?: string; } export interface Component { id: string; name: string; description?: string; props: ComponentProp[]; variants: Record<string, ComponentStory>; } export interface StorybookData { v: number; // v8 ~ entries?: { [key: string]: ComponentStory; }; // v7 stories?: { [key: string]: ComponentStory; }; } export const getComponents = async (storybookStaticDir: string): Promise<Component[]> => { try { const storiesJsonContent = await fs.readFile(storybookStaticDir, 'utf-8'); const storiesData: StorybookData = JSON.parse(storiesJsonContent); const components: Component[] = []; const storyEntries = storiesData.entries || storiesData.stories; for (const storyId in storyEntries) { const story = storyEntries[storyId]; if (!story.id) { continue; // Skip stories without a title } // example-button--primary => example-button const componentId = story.id.split('--')[0]; const componentName = componentId .replace(/-/g, '/') .replace(/(^|\/)(\w)/g, (_, separator, char) => `${separator}${char.toUpperCase()}`); // Check if the story already exists in the components array let component = components.find(c => c.name === story.title); if (!component) { component = { id: componentId, name: componentName, description: '', props: [], variants: {}, }; components.push(component); } if (component) { const storybookStaticDirname = path.dirname(storybookStaticDir); const storyFileFullPath = getAbsolutePath(path.resolve(storybookStaticDirname, '../', story.importPath || '')); const componentFullPath = getAbsolutePath(path.resolve(storybookStaticDirname, '../', story.componentPath || '')); component.variants[story.id] = { name: story.name, parameters: story.parameters, id: story.id, title: story.title, importPath: story.importPath, componentPath: story.componentPath, kind: story.kind, story: story.story, storyFileFullPath, componentFullPath, }; } } return components; } catch (error) { console.error('Error reading or parsing stories.json:', error); return []; } };

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/m-yoshiro/storybook-mcp'

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