Skip to main content
Glama
README.md
# forgekit-storybook-mcp

A **Model Context Protocol (MCP) server** for Storybook story generation, component analysis, and validation.

**Auto-detects** Next.js, Chakra UI, shadcn/ui, Tamagui, and Gluestack UI. Works with any React project — unrecognized frameworks use vanilla defaults.

---

## 🎉 What's New in v1.2

### ▲ Next.js support — first-class

- **Auto-detects Next.js projects** when `next` is in dependencies *and* a `next.config.{js,ts,mjs,cjs}` exists at the project root. Both signals must be present (avoids false positives from transitive `next` deps).
- **Scaffolds with `@storybook/nextjs`** — the official Storybook framework package — instead of `@storybook/react-vite`. Stories type-check against the Next.js framework's `StorybookConfig`, with `next/image`, `next/link`, `next/font`, and `next/navigation` working out of the box.
- **Generated stories import from `@storybook/nextjs`** — `import type { Meta, StoryObj } from '@storybook/nextjs'` for Next.js projects, `'@storybook/react'` for everything else.
- **No more `withRouter` decorator on Next.js stories** — `@storybook/nextjs` ships built-in mocks for `next/navigation` / `next/router` wired through `parameters.nextjs.appDirectory: true` in the generated `preview.tsx`.
- **UI lib + Next.js compose correctly** — Next.js + Chakra still emits `<ChakraProvider>` decorators in `preview.tsx`; Next.js wins the framework-package decision but the UI lib still drives the providers.
- New `isNextjs` field on `storybook-mcp.config.json` (persisted only when `true`).
- New `validator.ts` accepts `@storybook/react`, `@storybook/react-vite`, **and** `@storybook/nextjs` as valid Meta/StoryObj import sources.

### 🛠 Setup overhaul

- **Detected Storybook version is honored** — `runSetup` now reads the installed `storybook` version (from `node_modules` first, falling back to the `package.json` declared range) and pins all `@storybook/*` deps to that exact range. Older v9-and-below installs emit an upgrade `notice` instead of silently downgrading.
- **`@nx/storybook` peerDependency detection** — falls back to the Storybook version declared by Nx's plugin when no direct install is present.
- **Stricter detection contract** — exported `detectInstalledStorybookVersion()`, `detectNxStorybookVersion()`, and `detectNextjs()` for programmatic consumers.
- **`addons` are emitted explicitly** — `@storybook/addon-docs` and `@storybook/addon-a11y` now show up in `main.ts` for v10 (essentials/interactions are still bundled into the main `storybook` package).
- **shadcn detection casts a wider net** — matches on `components.json`, any `@radix-ui/*`, `@base-ui-components/react`, `class-variance-authority`, `tailwindcss`, *or* `lucide-react`.
- **Single source of truth** — `cli.ts` and `setup.ts` no longer duplicate framework detection; `cli.ts` now imports from `setup.ts`.

### ✅ Sync stays the final task

- `sync_all` continues to be the orchestrator that scans, generates, and tops up. Existing stories that lack interaction tests are *non-destructively* augmented by `appendMissingGeneratedStories` — your custom exports are preserved, missing template exports (Variants, Sizes, **Interactive play stories**) are appended.

### 🐛 Fixes

- TypeScript no longer errors on `NON_COMPONENT_FILES.includes(...)` — the `as const` array's narrowed type was rejecting arbitrary strings.

**Upgrading?** Run `npm install forgekit-storybook-mcp@latest` and `npx forgekit-storybook-mcp --setup --force` to refresh `.storybook/main.ts` if you want to opt into the Next.js framework package. No breaking changes for existing non-Next.js setups. See [CHANGELOG](./CHANGELOG.md) for full details.

---

## Table of Contents

- [Quick Start](#quick-start)
- [Prerequisites](#prerequisites)
- [Key Feature: Auto-Sync on Startup](#key-feature-auto-sync-on-startup)
- [Installation](#installation)
- [Configuration](#configuration)
- [CLI Flags](#cli-flags)
- [MCP Client Setup](#mcp-client-setup)
- [Tools Reference](#tools-reference)
- [Figma Integration](#figma-integration)
- [Templates](#templates)
- [Resources](#resources)
- [Programmatic Usage](#programmatic-usage)
- [Roadmap](#roadmap)
- [Related Projects](#related-projects)

---

## Quick Start

```bash
# 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:

```bash
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` ≥ 20
- `react` ≥ 18
- `react-dom` ≥ 18
- `storybook` ≥ 10.2
- `@storybook/react` ≥ 10.2
- `@storybook/react-vite` ≥ 10.2 (or `@storybook/react-webpack5` if using Webpack)

Install the core Storybook packages:

```bash
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:

1. **Scans** all components in configured libraries
2. **Creates** missing stories, tests, and MDX docs
3. **Updates** existing files when components have changed
4. **Caches** component hashes for efficient change detection

This means your Storybook documentation stays in sync with your components automatically.

---

## Installation

```bash
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:

```json
{
  "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`:

```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.json` dependencies (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

```bash
# 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

```bash
# 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`):

```json
{
  "mcpServers": {
    "forgekit-storybook": {
      "command": "npx",
      "args": ["forgekit-storybook-mcp"]
    }
  }
}
```

With CLI flags:

```json
{
  "mcpServers": {
    "forgekit-storybook": {
      "command": "npx",
      "args": ["forgekit-storybook-mcp", "--skip-init"]
    }
  }
}
```

If installed locally (faster startup):

```json
{
  "mcpServers": {
    "forgekit-storybook": {
      "command": "node",
      "args": ["node_modules/forgekit-storybook-mcp/dist/cli.js"]
    }
  }
}
```

### Claude Desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "forgekit-storybook": {
      "command": "npx",
      "args": ["forgekit-storybook-mcp"],
      "cwd": "/path/to/your/project"
    }
  }
}
```

---

## Tools Reference

| Tool | Description |
|------|-------------|
| [`list_components`](#list_components) | List all React components, filter by library or story status |
| [`analyze_component`](#analyze_component) | Extract props, dependencies, and get story suggestions |
| [`generate_story`](#generate_story) | Generate complete story files with variants and tests |
| [`update_story`](#update_story) | Regenerate a story while preserving your custom exports |
| [`generate_test`](#generate_test) | Generate Vitest or Playwright test files |
| [`generate_docs`](#generate_docs) | Generate MDX documentation |
| [`generate_code_connect`](#generate_code_connect) | Generate Figma Code Connect `.figma.tsx` files |
| [`validate_story`](#validate_story) | Check stories for best practices and issues |
| [`sync_all`](#sync_all) | Sync all components at once |
| [`sync_component`](#sync_component) | Sync a single component's story/test/docs |
| [`get_story_template`](#get_story_template) | Get a specific template |
| [`list_templates`](#list_templates) | List all available templates |
| [`get_component_coverage`](#get_component_coverage) | Get story coverage statistics |
| [`suggest_stories`](#suggest_stories) | Get prioritized list of components needing stories |
| [`check_health`](#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:**

```json
// 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:**

```json
{
  "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:**

```json
{
  "componentPath": "libs/ui/src/button/button.tsx"
}
```

**Response:**

```json
{
  "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](#templates)) |
| `overwrite` | `boolean` | - | `false` | Replace existing story file |
| `dryRun` | `boolean` | - | `false` | Preview without writing to disk |

**Examples:**

```json
// 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:**

```json
{
  "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:**

```json
// 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:**

```json
{
  "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:**

```json
// 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:**

```json
{
  "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:**

```json
{
  "storyPath": "libs/ui/src/button/button.stories.tsx"
}
```

**Response:**

```json
{
  "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:**

```json
// 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:**

```json
{
  "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:**

```json
// 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:**

```json
{
  "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](#templates)) |

**Example:**

```json
{
  "template": "with-msw"
}
```

**Response:**

```json
{
  "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:**

```json
{}
```

**Response:**

```json
{
  "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:**

```json
// Coverage for entire project
{}

// Coverage for "ui" library only
{
  "library": "ui"
}
```

**Response:**

```json
{
  "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:**

```json
// Get top 10 suggestions
{}

// Get top 5 suggestions from "ui" library
{
  "limit": 5,
  "library": "ui"
}
```

**Response:**

```json
{
  "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:**

```json
{}
```

**Response:**

```json
{
  "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:**

```json
{
  "componentPath": "src/components/Button.tsx"
}
```

**Response includes:**

- `preserved` — array of user-written story names that were kept
- `removed` — stories not in the merged output (usually empty)
- `validation.warnings` — non-blocking import warnings
- `summary` — 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:

```tsx
// ─── 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.json` on 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:**

```json
{
  "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`):

```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:**

```bash
npm install --save-dev @figma/code-connect
npx figma connect login
npx figma connect publish
```

See [Figma Integration](#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](https://www.figma.com/developers/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:**

1. Generate a Code Connect file for each component:

   ```json
   {
     "componentPath": "src/components/Button.tsx",
     "figmaNodeUrl": "https://figma.com/design/abc123/MyDesignSystem?node-id=1%3A2"
   }
   ```

2. Install and publish:

   ```bash
   npm install --save-dev @figma/code-connect
   npx figma connect login
   npx figma connect publish
   ```

3. 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`](https://npmjs.com/package/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-context` installed and running
- Local Storybook running (`npm run storybook`)

**Example call via `forgekit-context`:**

```json
{
  "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:

```tsx
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:

```tsx
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:

```tsx
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:

```tsx
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:

```typescript
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](./ROADMAP.md) for what's coming next — migration assistant, watch mode, Figma integration, and more.

---

## Related Projects

- [forgekit-figma-mcp](https://github.com/effinrich/forgekit-figma-mcp) — Figma design tokens → Chakra/Tailwind/Shadcn sync MCP
- [@storybook/addon-mcp](https://github.com/storybookjs/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