Forgekit Storybook
MCP server for Storybook story generation, component analysis, and validation
Ask AI about Forgekit Storybook
Powered by Claude Β· Grounded in docs
I know everything about Forgekit Storybook. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
forgekit-storybook-mcp
MIT Β· fully open source β story generation, tests, docs, templates, sync, Figma Code Connect
A Model Context Protocol (MCP) server for Storybook story generation, component analysis, and validation.
Auto-detects Chakra UI, shadcn/ui, Tamagui, and Gluestack UI. Works with any React project β unrecognized frameworks use vanilla defaults.
Why forgekit in the package name?
ForgeKit is the broader product that scaffolds Nx monorepos (app, UI library, Storybook, and related tooling). This MCP is meant to sit alongside that workflow as the Storybook + MCP piece. The @forgekit npm scope is not available, so the package is published as forgekit-storybook-mcp (unscoped). The CLI also exposes the shorter command storybook-mcp. This repo is MIT and standalone β you do not need the rest of the ForgeKit generator to use it.
π What's New in v1.2.0
MIT Β· fully open source
- No license keys or paid tiers β Polar validation, sync caps, and feature gating are removed. All tools (
sync_all,update_story,generate_code_connect, templates, etc.) are available under the MIT License.
CI & quality
- GitHub Actions runs
npm run verify(typecheck, build, tests, MCP stdio smoke) on every push and PR. npm run smoke/npm run verifyβ local gate before releases; smoke uses a tiny fixture and real MCPlistTools/callToolover stdio.--no-preflightβ skip Storybook/npm dependency checks (for automation and the smoke test). Normal runs still show install hints when deps are missing.
Earlier releases (background watch, .env loading, config auto-generation, concurrent sync, and more) are summarized in CHANGELOG.
Upgrading? Run npm install forgekit-storybook-mcp@latest. See CHANGELOG for migration notes by version.
Table of Contents
- Quick Start
- Prerequisites
- Key Feature: Auto-Sync on Startup
- Installation
- Configuration
- CLI Flags
- MCP Client Setup
- Tools Reference
- Figma Integration
- Templates
- Resources
- Programmatic Usage
- Roadmap
- Related Projects
Quick Start
# 1. Install the package
npm install forgekit-storybook-mcp
# 2. Create storybook-mcp.config.json in your project root (see Configuration)
# 3. Add to your MCP client (see MCP Client Setup below)
Prerequisites
Before installing, make sure your project has Storybook and its core dependencies set up.
Storybook
If you don't have Storybook yet:
npx storybook@latest init
This scaffolds the .storybook/ config directory, installs core packages, and adds example stories. Requires Storybook 10.2+. Earlier versions are not supported.
Required Packages
nodeβ₯ 20reactβ₯ 18react-domβ₯ 18storybookβ₯ 10.2@storybook/reactβ₯ 10.2@storybook/react-viteβ₯ 10.2 (or@storybook/react-webpack5if using Webpack)
Install the core Storybook packages:
npm i -D storybook@^10.2.0 @storybook/react@^10.2.0 @storybook/react-vite@^10.2.0
Recommended Addons
Some templates and features work best with these addons installed:
| Addon | Used By | Install |
|---|---|---|
storybook/test | Interactive templates, play functions | Included with storybook@10+ |
@storybook/addon-vitest | Testing (Vite projects) | npm i -D @storybook/addon-vitest |
@storybook/addon-a11y | Accessibility story generation | npm i -D @storybook/addon-a11y |
msw + msw-storybook-addon | with-msw template | npm i -D msw msw-storybook-addon |
@storybook/addon-interactions | Interaction testing panel | npm i -D @storybook/addon-interactions |
You don't need all of these upfront β the MCP will work without them and will suggest what to install when a template requires a missing dependency.
Key Feature: Auto-Sync on Startup
When the MCP server starts, it automatically:
- Scans all components in configured libraries
- Creates missing stories, tests, and MDX docs
- Updates existing files when components have changed
- Caches component hashes for efficient change detection
This means your Storybook documentation stays in sync with your components automatically.
Installation
npm install forgekit-storybook-mcp
# or
pnpm add forgekit-storybook-mcp
# or
yarn add forgekit-storybook-mcp
Configuration
You have three options for configuration, in order of priority:
Option 1: Config File (Recommended)
Create storybook-mcp.config.json in your project root:
{
"framework": "chakra",
"libraries": [
{
"name": "ui",
"path": "libs/ui/src",
"storyTitlePrefix": "UI",
"importAlias": "@ui"
},
{
"name": "shared",
"path": "libs/shared/src",
"storyTitlePrefix": "Shared",
"decorators": ["withRouter"]
}
],
"storyFilePattern": "**/*.stories.{ts,tsx}",
"componentPatterns": [
"**/src/**/*.tsx",
"!**/*.stories.tsx",
"!**/*.test.tsx"
],
"excludePatterns": ["**/node_modules/**", "**/dist/**"]
}
Option 2: package.json
Add a storybook-mcp field to your package.json:
{
"name": "my-app",
"storybook-mcp": {
"framework": "shadcn",
"libraries": [
{
"name": "components",
"path": "src/components",
"storyTitlePrefix": "Components"
}
]
}
}
Option 3: Auto-Detection
If no config is found, the MCP will auto-detect:
- Component directories:
src/components,libs/ui/src,packages/ui/src, etc. - Framework: Detected from your
package.jsondependencies (Chakra, shadcn, Tamagui, Gluestack)
Configuration Reference
| Property | Type | Default | Description |
|---|---|---|---|
rootDir | string | Auto-detected | Project root directory |
framework | string | 'vanilla' | UI framework: 'chakra', 'shadcn', 'tamagui', 'gluestack', 'vanilla' |
libraries | array | [] | Component library locations (see below) |
storyFilePattern | string | '**/*.stories.{ts,tsx}' | Glob pattern for story files |
componentPatterns | string[] | ['**/src/**/*.tsx', '!**/*.stories.tsx', '!**/*.test.tsx'] | Glob patterns for component files |
excludePatterns | string[] | ['**/node_modules/**', '**/dist/**'] | Directories to exclude |
templatesDir | string | - | Custom templates directory |
storybookVersion | number | 10 | Storybook version (10+ required) |
Library Configuration
Each library in the libraries array supports:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | β | Library identifier for filtering |
path | string | β | Path relative to rootDir |
storyTitlePrefix | string | β | Prefix for Storybook titles (e.g., "UI" β "UI/Button") |
decorators | string[] | - | Default decorators to apply to all stories |
importAlias | string | - | Import path alias (e.g., "@ui") |
CLI Flags
# Run with auto-sync (default behavior)
npx forgekit-storybook-mcp
# Skip auto-sync on startup - useful when you just want the MCP tools
npx forgekit-storybook-mcp --skip-init
# Preview what would be synced without writing any files
npx forgekit-storybook-mcp --dry-run
# Only run sync, then exit (useful for CI pipelines)
npx forgekit-storybook-mcp --init-only
# Disable specific generators during sync
npx forgekit-storybook-mcp --no-stories # Don't generate story files
npx forgekit-storybook-mcp --no-tests # Don't generate test files
npx forgekit-storybook-mcp --no-docs # Don't generate MDX docs
# Only create missing files, don't update existing ones
npx forgekit-storybook-mcp --no-update
# Force overwrite existing story/test/doc files
npx forgekit-storybook-mcp --force
# Only sync a specific library (must match library.name in config)
npx forgekit-storybook-mcp --lib=ui
# Run interactive setup wizard (creates storybook-mcp.config.json)
npx forgekit-storybook-mcp --setup
Combining Flags
# CI pipeline: sync stories only, exit when done
npx forgekit-storybook-mcp --init-only --no-tests --no-docs
# Development: skip sync, just run the MCP server
npx forgekit-storybook-mcp --skip-init
# Preview: see what would change without modifying files
npx forgekit-storybook-mcp --dry-run --no-update
# Force-regenerate only the ui library, then exit
npx forgekit-storybook-mcp --init-only --force --lib=ui
MCP Client Setup
Cursor / VS Code
Add to .cursor/mcp.json (or .vscode/mcp.json):
{
"mcpServers": {
"forgekit-storybook": {
"command": "npx",
"args": ["forgekit-storybook-mcp"]
}
}
}
With CLI flags:
{
"mcpServers": {
"forgekit-storybook": {
"command": "npx",
"args": ["forgekit-storybook-mcp", "--skip-init"]
}
}
}
If installed locally (faster startup):
{
"mcpServers": {
"forgekit-storybook": {
"command": "node",
"args": ["node_modules/forgekit-storybook-mcp/dist/cli.js"]
}
}
}
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"forgekit-storybook": {
"command": "npx",
"args": ["forgekit-storybook-mcp"],
"cwd": "/path/to/your/project"
}
}
}
Tools Reference
| Tool | Description |
|---|---|
list_components | List all React components, filter by library or story status |
analyze_component | Extract props, dependencies, and get story suggestions |
generate_story | Generate complete story files with variants and tests |
update_story | Regenerate a story while preserving your custom exports |
generate_test | Generate Vitest or Playwright test files |
generate_docs | Generate MDX documentation |
generate_code_connect | Generate Figma Code Connect .figma.tsx files |
validate_story | Check stories for best practices and issues |
sync_all | Sync all components at once |
sync_component | Sync a single component's story/test/docs |
get_story_template | Get a specific template |
list_templates | List all available templates |
get_component_coverage | Get story coverage statistics |
suggest_stories | Get prioritized list of components needing stories |
check_health | Check Storybook installation health |
list_components
List all React components in configured libraries.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
library | string | - | all | Filter by library name |
hasStory | boolean | - | all | Filter by story status: true = only with stories, false = only without |
Examples:
// List ALL components across all libraries
{}
// List only components in the "ui" library
{
"library": "ui"
}
// List components that DON'T have stories yet
{
"hasStory": false
}
// List components in "shared" library that need stories
{
"library": "shared",
"hasStory": false
}
Response:
{
"components": [
{
"name": "Button",
"filePath": "libs/ui/src/button/button.tsx",
"library": "ui",
"hasStory": false,
"exportType": "named"
},
{
"name": "Card",
"filePath": "libs/ui/src/card/card.tsx",
"library": "ui",
"hasStory": true,
"storyPath": "libs/ui/src/card/card.stories.tsx",
"exportType": "default"
}
],
"total": 2,
"withStories": 1,
"withoutStories": 1,
"summary": "Found 2 components: 1 with stories, 1 without stories"
}
analyze_component
Analyze a React component to extract its structure, props, and dependencies.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
componentPath | string | β | Path to the component file |
Example:
{
"componentPath": "libs/ui/src/button/button.tsx"
}
Response:
{
"analysis": {
"name": "Button",
"filePath": "libs/ui/src/button/button.tsx",
"library": "ui",
"hasStory": false,
"exportType": "named",
"props": [
{
"name": "variant",
"type": "'solid' | 'outline' | 'ghost'",
"required": false,
"defaultValue": "'solid'",
"description": "Visual style variant",
"controlType": "select",
"controlOptions": ["solid", "outline", "ghost"]
},
{
"name": "size",
"type": "'sm' | 'md' | 'lg'",
"required": false,
"defaultValue": "'md'",
"controlType": "select",
"controlOptions": ["sm", "md", "lg"]
},
{
"name": "disabled",
"type": "boolean",
"required": false,
"defaultValue": "false",
"controlType": "boolean"
},
{
"name": "children",
"type": "ReactNode",
"required": true,
"controlType": "text"
}
],
"dependencies": {
"usesRouter": false,
"usesReactQuery": false,
"usesChakra": true,
"usesGluestack": false,
"usesReactNative": false,
"usesEmotion": false,
"usesTailwind": false,
"usesFramerMotion": true,
"usesMSW": false,
"usesGlobalState": false,
"otherImports": ["@chakra-ui/react", "framer-motion"]
},
"suggestions": [
"Use 'with-variants' template to showcase all size/variant combinations",
"Add Framer Motion decorator for animation testing",
"Consider adding interactive tests for click/focus states"
],
"sourcePreview": "export const Button = ({ variant = 'solid', size = 'md', ... }) => { ... }"
},
"summary": "Analyzed Button: 4 props, no story",
"recommendations": [
"Use 'with-variants' template to showcase all size/variant combinations",
"Add Framer Motion decorator for animation testing"
]
}
generate_story
Generate a Storybook story file for a component.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
componentPath | string | β | - | Path to the component file |
includeVariants | boolean | - | true | Add stories showcasing all size/variant combinations |
includeInteractive | boolean | - | true | Add play function tests for user interactions |
includeA11y | boolean | - | false | Add accessibility test story |
includeResponsive | boolean | - | false | Add mobile/tablet/desktop viewport stories |
template | string | - | auto | Template to use (see Templates) |
overwrite | boolean | - | false | Replace existing story file |
dryRun | boolean | - | false | Preview without writing to disk |
Examples:
// Basic: generate with defaults (variants + interactive)
{
"componentPath": "libs/ui/src/button/button.tsx"
}
// Minimal: just the basic story, no extras
{
"componentPath": "libs/ui/src/button/button.tsx",
"includeVariants": false,
"includeInteractive": false
}
// Full coverage: everything including a11y and responsive
{
"componentPath": "libs/ui/src/button/button.tsx",
"includeVariants": true,
"includeInteractive": true,
"includeA11y": true,
"includeResponsive": true
}
// Use a specific template
{
"componentPath": "libs/ui/src/user-list/user-list.tsx",
"template": "with-msw"
}
// Preview what would be generated
{
"componentPath": "libs/ui/src/button/button.tsx",
"dryRun": true
}
// Replace an existing story
{
"componentPath": "libs/ui/src/button/button.tsx",
"overwrite": true
}
Response:
{
"story": {
"content": "import type { Meta, StoryObj } from '@storybook/react'\nimport { Button } from './Button'\n\nconst meta: Meta<typeof Button> = {\n title: 'Components/Button',\n component: Button,\n tags: [],\n ...\n}\n\nexport default meta\ntype Story = StoryObj<typeof Button>\n\nexport const Default: Story = { ... }\nexport const Sizes: Story = { ... }\nexport const Variants: Story = { ... }",
"filePath": "libs/ui/src/button/button.stories.tsx",
"imports": ["@storybook/react", "./Button"],
"stories": ["Default", "Sizes", "Variants", "ClickTest"],
"warnings": []
},
"written": true,
"path": "libs/ui/src/button/button.stories.tsx",
"summary": "Created story at libs/ui/src/button/button.stories.tsx"
}
generate_test
Generate a test file for a component. Uses vitest + @testing-library by default. Uses Playwright only if @playwright/test is in your project's dependencies.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
componentPath | string | β | - | Path to the component file |
overwrite | boolean | - | false | Replace existing test file |
dryRun | boolean | - | false | Preview without writing to disk |
Examples:
// Generate test for a component
{
"componentPath": "libs/ui/src/button/button.tsx"
}
// Preview without writing
{
"componentPath": "libs/ui/src/button/button.tsx",
"dryRun": true
}
// Replace existing test
{
"componentPath": "libs/ui/src/button/button.tsx",
"overwrite": true
}
Response:
{
"test": {
"content": "import { describe, it, expect } from 'vitest'\nimport { render, screen } from '@testing-library/react'\nimport { Button } from './Button'\n\ndescribe('Button', () => {\n it('renders correctly', () => {\n render(<Button>Click me</Button>)\n expect(screen.getByText('Click me')).toBeInTheDocument()\n })\n})\n...",
"filePath": "libs/ui/src/button/button.test.tsx"
},
"written": true,
"path": "libs/ui/src/button/button.test.tsx",
"summary": "Created test at libs/ui/src/button/button.test.tsx"
}
generate_docs
Generate MDX documentation for a component.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
componentPath | string | β | - | Path to the component file |
overwrite | boolean | - | false | Replace existing docs file |
dryRun | boolean | - | false | Preview without writing to disk |
Examples:
// Generate docs for a component
{
"componentPath": "libs/ui/src/button/button.tsx"
}
// Preview without writing
{
"componentPath": "libs/ui/src/button/button.tsx",
"dryRun": true
}
Response:
{
"docs": {
"content": "import { Canvas, Meta, ArgTypes } from '@storybook/blocks'\nimport * as ButtonStories from './Button.stories'\n\n<Meta of={ButtonStories} />\n\n# Button\n\n## Usage\n\n<Canvas of={ButtonStories.Default} />\n\n## Props\n\n<ArgTypes of={ButtonStories} />\n...",
"filePath": "libs/ui/src/button/button.mdx"
},
"written": true,
"path": "libs/ui/src/button/button.mdx",
"summary": "Created docs at libs/ui/src/button/button.mdx"
}
validate_story
Validate an existing story file for best practices and issues.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
storyPath | string | β | Path to the story file |
Example:
{
"storyPath": "libs/ui/src/button/button.stories.tsx"
}
Response:
{
"validation": {
"valid": false,
"score": 72,
"errors": [
{
"type": "error",
"code": "MISSING_META_TITLE",
"message": "Story is missing a title in meta",
"line": 5,
"fix": "Add 'title' property to meta object"
}
],
"warnings": [],
"suggestions": [
{
"type": "suggestion",
"code": "ADD_PLAY_FUNCTION",
"message": "Consider adding interaction tests with play functions",
"fix": "Add a story with a play function for testing user interactions"
}
]
},
"summary": "Story has 1 errors (score: 72/100)"
}
sync_all
Sync all components - create missing stories/tests/docs and update changed ones.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
library | string | - | all | Only sync components in this library |
generateStories | boolean | - | true | Generate story files |
generateTests | boolean | - | true | Generate test files |
generateDocs | boolean | - | true | Generate MDX docs |
updateExisting | boolean | - | true | Update files when components change |
dryRun | boolean | - | false | Preview without writing to disk |
Examples:
// Sync everything with defaults
{}
// Sync only the "ui" library
{
"library": "ui"
}
// Only generate stories, no tests or docs
{
"generateStories": true,
"generateTests": false,
"generateDocs": false
}
// Only create missing files, don't update existing
{
"updateExisting": false
}
// Preview what would change
{
"dryRun": true
}
// Sync only stories for "shared" library, don't update existing
{
"library": "shared",
"generateStories": true,
"generateTests": false,
"generateDocs": false,
"updateExisting": false
}
Response:
{
"scanned": 24,
"created": {
"stories": 8,
"tests": 8,
"docs": 8
},
"updated": {
"stories": 3,
"tests": 2,
"docs": 3
},
"skipped": 0,
"errors": [],
"summary": "Synced 24 components: Created 8 stories, 8 tests, 8 docs. Updated 8 files."
}
sync_component
Sync a single component's story, test, and docs.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
componentPath | string | β | - | Path to the component file |
generateStories | boolean | - | true | Generate story file |
generateTests | boolean | - | true | Generate test file |
generateDocs | boolean | - | true | Generate MDX docs |
dryRun | boolean | - | false | Preview without writing to disk |
Examples:
// Sync everything for one component
{
"componentPath": "libs/ui/src/button/button.tsx"
}
// Only sync the story, not tests or docs
{
"componentPath": "libs/ui/src/button/button.tsx",
"generateStories": true,
"generateTests": false,
"generateDocs": false
}
// Preview what would change
{
"componentPath": "libs/ui/src/button/button.tsx",
"dryRun": true
}
Response:
{
"result": {
"component": "Button",
"story": {
"action": "created",
"path": "libs/ui/src/button/button.stories.tsx"
},
"test": {
"action": "created",
"path": "libs/ui/src/button/button.test.tsx"
},
"docs": {
"action": "skipped",
"path": "libs/ui/src/button/button.mdx",
"reason": "Already exists and unchanged"
}
},
"summary": "Button: story: created, test: created"
}
get_story_template
Get a specific template by name.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
template | string | β | Template name (see Templates) |
Example:
{
"template": "with-msw"
}
Response:
{
"template": {
"name": "with-msw",
"description": "Story with MSW API mocking",
"useCase": "Components that fetch data and need mocked API responses",
"content": "import type { Meta, StoryObj } from '@storybook/react'\nimport { http, HttpResponse } from 'msw'\nimport { {{ComponentName}} } from './{{ComponentName}}'\n\nconst meta: Meta<typeof {{ComponentName}}> = {\n title: 'Components/{{ComponentName}}',\n component: {{ComponentName}},\n tags: [],\n}\n\nexport default meta\ntype Story = StoryObj<typeof {{ComponentName}}>\n\nexport const Default: Story = {\n parameters: {\n msw: {\n handlers: [\n http.get('/api/data', () => {\n return HttpResponse.json({\n items: [\n { id: 1, name: 'Item 1' },\n ],\n })\n }),\n ],\n },\n },\n}\n...",
"placeholders": ["ComponentName", "component-name"]
},
"usage": "Replace placeholders: ComponentName, component-name"
}
list_templates
List all available story templates.
Parameters: None
Example:
{}
Response:
{
"templates": [
{
"name": "basic",
"description": "Simple story with basic args",
"useCase": "Quick component documentation with minimal setup",
"available": true
},
{
"name": "with-controls",
"description": "Story with full argTypes controls",
"useCase": "Interactive component exploration with all props exposed",
"available": true
},
{
"name": "with-variants",
"description": "Story showcasing all variants and sizes",
"useCase": "Design system documentation showing all visual options",
"available": true
}
],
"count": 8
}
get_component_coverage
Get story coverage statistics for the project.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
library | string | - | all | Filter by library name |
Examples:
// Coverage for entire project
{}
// Coverage for "ui" library only
{
"library": "ui"
}
Response:
{
"total": 24,
"withStories": 16,
"withoutStories": 8,
"coverage": "67%",
"byLibrary": {
"ui": {
"total": 15,
"withStories": 12
},
"shared": {
"total": 9,
"withStories": 4
}
},
"componentsNeedingStories": [
{
"name": "Tooltip",
"path": "libs/ui/src/tooltip/tooltip.tsx",
"library": "ui"
},
{
"name": "DataTable",
"path": "libs/shared/src/data-table/data-table.tsx",
"library": "shared"
}
]
}
suggest_stories
Get a prioritized list of components that need stories.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | - | 10 | Max number of suggestions |
library | string | - | all | Filter by library name |
Examples:
// Get top 10 suggestions
{}
// Get top 5 suggestions from "ui" library
{
"limit": 5,
"library": "ui"
}
Response:
{
"suggestions": [
{
"component": "Button",
"path": "libs/ui/src/button/button.tsx",
"library": "ui",
"command": "generate_story with componentPath: \"libs/ui/src/button/button.tsx\""
},
{
"component": "Card",
"path": "libs/ui/src/card/card.tsx",
"library": "ui",
"command": "generate_story with componentPath: \"libs/ui/src/card/card.tsx\""
}
],
"total": 8,
"showing": 2,
"summary": "8 components without stories. Showing top 2."
}
check_health
Check Storybook installation health β missing packages, outdated configs, and version mismatches. Useful for diagnosing setup issues, especially when migrating to Storybook 10.
Parameters: None
Example:
{}
Response:
{
"passed": false,
"checks": [
{ "name": "package:storybook", "status": "pass", "message": "storybook is installed" },
{ "name": "config:main:addon:@storybook/addon-essentials", "status": "warn", "message": "@storybook/addon-essentials is bundled into storybook in v10 β can be removed from addons list", "fix": "Remove '@storybook/addon-essentials' from addons array in .storybook/main" }
],
"installCommands": [],
"summary": "Preflight: 1 warning(s) out of 8 checks"
}
update_story
Regenerate a story file using the latest component analysis while preserving any exports you have written by hand.
Unlike generate_story with overwrite: true (which clobbers everything), update_story detects which export const X: Story blocks you added and appends them below the regenerated content, separated by a comment marker.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
componentPath | string | required | Path to the component file |
includeVariants | boolean | true | Regenerate variant stories |
includeInteractive | boolean | true | Regenerate play function tests |
includeA11y | boolean | false | Regenerate accessibility stories |
includeResponsive | boolean | false | Regenerate viewport stories |
template | string | auto | Specific template to use |
dryRun | boolean | false | Preview merged result without writing |
Example:
{
"componentPath": "src/components/Button.tsx"
}
Response includes:
preservedβ array of user-written story names that were keptremovedβ stories not in the merged output (usually empty)validation.warningsβ non-blocking import warningssummaryβ human-readable result with list of preserved stories
How it works:
The tool looks for export const X: Story blocks that are not in the freshly generated content. Those are your custom stories. They get appended after a separator:
// βββ User-added stories (preserved by update_story) βββ
export const MyEdgeCase: Story = {
args: { label: 'Edge case' },
}
Note: A version entry is recorded in
.forgekit/story-history.jsonon every write (action:merged).
generate_code_connect
Generate a @figma/code-connect .figma.tsx file that links your component to Figma Dev Mode.
When published with npx figma connect publish, designers inspecting your component in Figma see your real React code β props, variants, and usage examples β instead of auto-generated snippets.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
componentPath | string | required | Path to the component file |
figmaNodeUrl | string | β | Figma component URL (https://figma.com/design/<fileId>/...?node-id=...). Omit to use a placeholder. |
overwrite | boolean | false | Replace existing .figma.tsx file |
dryRun | boolean | false | Preview output without writing |
Example:
{
"componentPath": "src/components/Button.tsx",
"figmaNodeUrl": "https://figma.com/design/abc123/MyDesignSystem?node-id=1%3A2"
}
Prop type mapping:
| TypeScript type | Figma binding |
|---|---|
string | figma.string('PropName') |
boolean | figma.boolean('PropName') |
'a' | 'b' union | figma.enum('PropName', { a: 'a', b: 'b' }) |
ReactNode / children | figma.children(['*']) |
number | figma.number('PropName') |
Event handlers, className, style, and ref are excluded automatically.
Generated output (src/components/Button.figma.tsx):
import figma from '@figma/code-connect/react'
import { Button } from './Button'
figma.connect(Button, 'https://figma.com/design/abc123/MyDesignSystem?node-id=1%3A2', {
props: {
variant: figma.enum('Variant', {
"primary": "primary",
"secondary": "secondary",
"ghost": "ghost",
}),
disabled: figma.boolean('Disabled'),
children: figma.children(['*']),
},
example: ({ variant, disabled, children }) => (
<Button variant={variant} disabled={disabled}>{children}</Button>
),
})
Publishing to Figma:
npm install --save-dev @figma/code-connect
npx figma connect login
npx figma connect publish
See Figma Integration for the full workflow.
Figma Integration
This project supports two complementary Figma integrations:
Code Connect β link components to Figma Dev Mode
Figma Code Connect attaches your real React component to a Figma component. Designers in Dev Mode see your actual props, variants, and a working code example instead of placeholder snippets.
Workflow:
-
Generate a Code Connect file for each component:
{ "componentPath": "src/components/Button.tsx", "figmaNodeUrl": "https://figma.com/design/abc123/MyDesignSystem?node-id=1%3A2" } -
Install and publish:
npm install --save-dev @figma/code-connect npx figma connect login npx figma connect publish -
Open the component in Figma β Dev Mode β Code panel. Your component code appears.
Tip: Copy the Figma node URL by right-clicking a component on the canvas β "Copy link".
Code to Canvas β push story renders into Figma (via forgekit-context)
The sync_stories_to_figma tool (part of forgekit-context) connects to the Figma desktop app's Dev Mode MCP server and pushes each component's Default story as an editable frame on your canvas.
Requirements:
- Figma desktop app (not browser)
- Dev Mode MCP server enabled: Figma menu β Preferences β Enable Dev Mode MCP Server
forgekit-contextinstalled and running- Local Storybook running (
npm run storybook)
Example call via forgekit-context:
{
"storybookUrl": "http://localhost:6006",
"dryRun": true
}
Remove dryRun to push stories to the canvas.
Templates
Templates are pre-built story structures for different use cases. Use them with the template parameter in generate_story.
| Template | Use Case | Example |
|---|---|---|
basic | Quick documentation, minimal setup | Simple presentational components |
with-controls | Interactive exploration with all props | Design system components |
with-variants | Showcase all sizes/variants | Buttons, badges, avatars |
with-msw | Components that fetch data | User lists, dashboards |
with-router | Components using React Router | Navigation, breadcrumbs |
page | Full-page components | Landing pages, dashboards |
interactive | Components with user interactions | Forms, modals, dropdowns |
form | Form components with validation | Login forms, settings panels |
Template Examples
basic - Minimal setup:
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
tags: [],
}
export default meta
type Story = StoryObj<typeof Button>
export const Default: Story = {
args: {
children: 'Button content',
},
}
with-variants - Showcase all combinations:
export const Sizes: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</div>
),
}
export const AllVariants: Story = {
render: () => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{(['solid', 'outline', 'ghost'] as const).map((variant) => (
<div key={variant} style={{ display: 'flex', gap: '1rem' }}>
<Button variant={variant} size="sm">Small</Button>
<Button variant={variant} size="md">Medium</Button>
<Button variant={variant} size="lg">Large</Button>
</div>
))}
</div>
),
}
with-msw - Mock API responses:
import { http, HttpResponse } from 'msw'
export const Default: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => {
return HttpResponse.json({
users: [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
],
})
}),
],
},
},
}
export const Loading: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', async () => {
await new Promise((r) => setTimeout(r, 5000))
return HttpResponse.json({})
}),
],
},
},
}
export const Error: Story = {
parameters: {
msw: {
handlers: [
http.get('/api/users', () => {
return HttpResponse.json({ error: 'Failed' }, { status: 500 })
}),
],
},
},
}
interactive - Play function tests:
import { expect, userEvent, within } from 'storybook/test'
export const ClickTest: Story = {
args: { children: 'Click me' },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const button = canvas.getByText(/click me/i)
await expect(button).toBeInTheDocument()
await userEvent.click(button)
// Add assertions for post-click state
},
}
export const KeyboardNavigation: Story = {
args: { children: 'Focus me' },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const button = canvas.getByText(/focus me/i)
await userEvent.tab()
await expect(button).toHaveFocus()
await userEvent.keyboard('{Enter}')
},
}
Resources
The MCP provides these read-only resources:
| Resource URI | Description |
|---|---|
storybook://libraries | Configured library information |
storybook://patterns | Common story patterns and best practices |
storybook://config | Current MCP configuration |
Programmatic Usage
You can also use the MCP server programmatically:
import { createStorybookMCPServer } from 'forgekit-storybook-mcp'
const server = createStorybookMCPServer({
rootDir: process.cwd(),
framework: 'chakra',
libraries: [
{
name: 'ui',
path: 'src/components',
storyTitlePrefix: 'UI',
importAlias: '@ui',
},
],
storybookVersion: 10,
})
// Server is now ready to handle MCP requests
Roadmap
See ROADMAP.md for what's coming next β migration assistant, watch mode, Figma integration, and more.
Related Projects
- forgekit-figma-mcp β Figma design tokens β Chakra/Tailwind/Shadcn sync MCP
- @storybook/addon-mcp - Official Storybook MCP (reads stories)
This MCP focuses on generating stories, while the official one focuses on reading existing Storybook data. They complement each other.
License
MIT
