Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nine-jars-send.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@storybook/mcp': patch
---

Add optional "enabled" function to the directly exported tool adders. This allow you to define a function that will dynamically enable/disable the tool however you want, eg. per request condition
5 changes: 5 additions & 0 deletions .changeset/tame-deer-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@storybook/addon-mcp': patch
---

Add possibility to configure toolsets (dev tools vs docs tools) either via addon options or request headers
32 changes: 32 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,38 @@ The addon uses a **Vite plugin workaround** to inject middleware (see `packages/
- Solution: Inject a Vite plugin via `viteFinal` that adds `/mcp` endpoint
- Handler in `mcp-handler.ts` creates MCP servers using `tmcp` with HTTP transport

**Toolset Configuration:**

The addon supports configuring which toolsets are enabled:

- **Addon Options**: Configure default toolsets in `.storybook/main.js`:
```typescript
{
name: '@storybook/addon-mcp',
options: {
toolsets: {
dev: true, // get-story-urls, get-ui-building-instructions
docs: true, // list-all-components, get-component-documentation
}
}
}
```
- **Per-Request Override**: MCP clients can override toolsets per-request using the `X-MCP-Toolsets` header:
- Header format: comma-separated list (e.g., `dev,docs`)
- When header is present, only specified toolsets are enabled (others are disabled)
- When header is absent, addon options are used
- **Tool Enablement**: Tools use the `enabled` callback to check if their toolset is active:
```typescript
server.tool(
{
name: 'my-tool',
enabled: () => server.ctx.custom?.toolsets?.dev ?? true,
},
handler,
);
```
- **Context-Aware**: The `getToolsets()` function in `mcp-handler.ts` parses the header and returns enabled toolsets, which are passed to tools via `AddonContext.toolsets`

### MCP Library Architecture

The `@storybook/mcp` package (in `packages/mcp`) is framework-agnostic:
Expand Down
87 changes: 80 additions & 7 deletions .github/instructions/addon-mcp.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,61 @@ This is a Storybook addon that runs an MCP (Model Context Protocol) server withi
- **MCP Server**: Built using `tmcp` library with HTTP transport
- **Vite Plugin Middleware**: Workaround to inject `/mcp` endpoint into Storybook's dev server
- **Tools System**: Extensible tool registration using `server.tool()` method
- **Toolsets Configuration**: Configurable tool groups that can be toggled via addon options or per-request headers
- **Context-Based Architecture**: AddonContext passed through to all tools containing Storybook options and runtime info
- **Schema Validation**: Uses Valibot for JSON schema validation via `@tmcp/adapter-valibot`
- **Telemetry**: Usage tracking with opt-out via Storybook's `disableTelemetry` config

### Toolsets

The addon supports two toolsets that can be enabled/disabled:

1. **`dev`** (default: true)
- `get-story-urls`: Retrieve story URLs from Storybook
- `get-ui-building-instructions`: Provide UI development guidelines

2. **`docs`** (default: true)
- `list-all-components`: List all available components from manifest
- `get-component-documentation`: Get detailed component documentation
- Requires experimental feature flag `features.experimentalComponentsManifest`

**Configuration Methods:**

1. **Addon Options** (`.storybook/main.js`):

```typescript
{
name: '@storybook/addon-mcp',
options: {
toolsets: {
dev: true,
docs: true,
}
}
}
```

2. **Per-Request Header** (`X-MCP-Toolsets`):
- Comma-separated list of toolset names
- Example: `X-MCP-Toolsets: dev,docs`
- When present, overrides addon options (all toolsets default to disabled except those in header)
- When absent, uses addon options

**Implementation:**

- `getToolsets()` function in `mcp-handler.ts` parses the header and merges with addon options
- Tools use `enabled` callback to check if their toolset is active:
```typescript
server.tool(
{
name: 'my-tool',
enabled: () => server.ctx.custom?.toolsets?.dev ?? true,
},
handler,
);
```
- `AddonContext.toolsets` contains the resolved toolset configuration

### File Structure

```
Expand Down Expand Up @@ -250,6 +301,8 @@ To add a new MCP tool to the addon:
title: 'My Tool',
description: 'What this tool does',
schema: MyToolInput,
// Optional: Enable/disable based on toolset configuration
enabled: () => server.ctx.custom?.toolsets?.dev ?? true,
},
async (input: MyToolInput) => {
try {
Expand All @@ -267,7 +320,7 @@ To add a new MCP tool to the addon:
}
```

5. Import and register in `src/mcp-handler.ts` within `createAddonMcpHandler()`:
5. Import and register in `src/mcp-handler.ts` within `initializeMCPServer()`:

```typescript
import { addMyTool } from './tools/my-tool.ts';
Expand All @@ -276,7 +329,26 @@ To add a new MCP tool to the addon:
await addMyTool(server);
```

6. Add telemetry tracking if needed (see existing tools for examples)
6. If adding a new toolset, update the `AddonOptions` schema in `src/types.ts` to include the new toolset:

```typescript
export const AddonOptions = v.object({
toolsets: v.optional(
v.object({
dev: v.exactOptional(v.boolean(), true),
docs: v.exactOptional(v.boolean(), true),
myNewToolset: v.exactOptional(v.boolean(), true), // Add your toolset
}),
{
dev: true,
docs: true,
myNewToolset: true,
},
),
});
```

7. Add telemetry tracking if needed (see existing tools for examples)

## Storybook Integration

Expand Down Expand Up @@ -366,7 +438,11 @@ const addonContext: AddonContext = {
});
}
},
onGetComponentDocumentation: async ({ input, foundComponents, notFoundIds }) => {
onGetComponentDocumentation: async ({
input,
foundComponents,
notFoundIds,
}) => {
if (!disableTelemetry && server) {
await collectTelemetry({
event: 'tool:getComponentDocumentation',
Expand All @@ -383,10 +459,7 @@ const addonContext: AddonContext = {
Telemetry respects Storybook's `disableTelemetry` config:

```typescript
const { disableTelemetry } = await options.presets.apply<CoreConfig>(
'core',
{},
);
const { disableTelemetry } = options as CoreConfig;
```

Users can opt out by setting `disableTelemetry: true` in their Storybook config.
Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,39 @@ on:
branches:
- main

permissions:
id-token: write # used to upload artifacts to codecov

jobs:
build-mcp:
name: Build @storybook/mcp
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2 # see https://docs.codecov.com/docs/environment-specific-requirements#github-actions

- name: Setup Node.js and Install Dependencies
uses: ./.github/actions/setup-node-and-install

- name: Build
run: pnpm build --filter @storybook/mcp
run: pnpm build --env-mode=loose --filter @storybook/mcp

build-addon-mcp:
name: Build @storybook/addon-mcp
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2 # see https://docs.codecov.com/docs/environment-specific-requirements#github-actions

- name: Setup Node.js and Install Dependencies
uses: ./.github/actions/setup-node-and-install

- name: Build
run: pnpm build --filter @storybook/addon-mcp
run: pnpm build --env-mode=loose --filter @storybook/addon-mcp

build-storybook:
name: Build internal Storybook
Expand Down Expand Up @@ -151,15 +158,15 @@ jobs:
run: pnpm format --check

collect-coverage:
permissions:
id-token: write
name: Collect coverage and test results
runs-on: ubuntu-latest
needs: [test-mcp, test-addon-mcp]
if: always()
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 2 # see https://docs.codecov.com/docs/environment-specific-requirements#github-actions

- name: Download @storybook/mcp coverage
uses: actions/download-artifact@v4
Expand Down
13 changes: 12 additions & 1 deletion apps/internal-storybook/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@ const config = defineMain({
files: '**/*.stories.@(js|jsx|ts|tsx)',
},
],
addons: ['@storybook/addon-docs', '@storybook/addon-mcp'],
addons: [
'@storybook/addon-docs',
{
name: '@storybook/addon-mcp',
options: {
// toolsets: {
// dev: false,
// docs: false,
// },
},
},
],
framework: '@storybook/react-vite',
logLevel: 'debug',
core: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"devDependencies": {
"@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.29.6",
"@codecov/rollup-plugin": "^1.9.1",
"@codecov/vite-plugin": "^1.9.1",
"@modelcontextprotocol/inspector": "^0.16.5",
"pkg-pr-new": "^0.0.57",
Expand Down
50 changes: 48 additions & 2 deletions packages/addon-mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,52 @@ npm run storybook

The MCP server will be available at `<your_storybook_dev_server_origin>/mcp` when Storybook is running.

### Configuration

#### Addon Options

You can configure which toolsets are enabled by default in your `.storybook/main.js`:

```javascript
// .storybook/main.js
export default {
addons: [
{
name: '@storybook/addon-mcp',
options: {
toolsets: {
dev: true, // Tools for story URL retrieval and UI building instructions (default: true)
docs: true, // Tools for component manifest and documentation (default: true, requires experimental feature)
},
},
},
],
};
```

**Available Toolsets:**

- `dev`: Enables [Dev Tools](#dev-tools)
- `docs`: Enables [Documentation Tools](#docs-tools-experimental)

Disabling the Dev Tools is useful when you want to try out the same experience that your external component consumers will get, because they only get the Component Documentation Tools.

#### Configuring toolsets with headers

You can also configure the available toolsets when setting up the MCP Server in your MCP Client by setting the `X-MCP-Toolsets` header. The header is a comma-separated list of toolset names, `X-MCP-Toolsets: dev,docs`. Eg. to configure your client to only have the Component Documentation Tools, the `.mcp.json`-file could look like this (format depends on the exact client you're using):

```json
{
"storybook-mcp": {
"url": "http://localhost:6006/mcp",
"type": "http",
"headers": {
"X-MCP-Toolsets": "docs"
}
}
}
```

### Configuring Your Agent

> [!NOTE]
Expand Down Expand Up @@ -88,7 +134,7 @@ This addon provides MCP tools that your agent can use. The goal is that the agen

**If you are prompting from an IDE like VSCode or Cursor, be sure to use `Agent` mode and `sonnet-4.5` or better.**

### Core Tools
### Dev Tools

These tools are always available when the addon is installed:

Expand Down Expand Up @@ -119,7 +165,7 @@ Agent calls tool, gets response:
http://localhost:6006/?path=/story/example-button--primary
```

### Component Documentation Tools (Experimental)
### Docs Tools (Experimental)

These additional tools are available when the **experimental** component manifest feature is enabled. They provide agents with detailed documentation about your UI components.

Expand Down
Loading
Loading