@storybook/addon-mcp 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -3
- package/dist/preset.js +61 -14
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The addon provides tools to improve agents' UI development capabilities, retriev
|
|
|
15
15
|
### Installation and Setup
|
|
16
16
|
|
|
17
17
|
> [!NOTE]
|
|
18
|
-
> This addon requires Storybook
|
|
18
|
+
> This addon requires Storybook version 9.1.16 or higher.
|
|
19
19
|
|
|
20
20
|
Use Storybook's CLI to automatically install and configure the addon:
|
|
21
21
|
|
|
@@ -33,6 +33,52 @@ npm run storybook
|
|
|
33
33
|
|
|
34
34
|
The MCP server will be available at `<your_storybook_dev_server_origin>/mcp` when Storybook is running.
|
|
35
35
|
|
|
36
|
+
### Configuration
|
|
37
|
+
|
|
38
|
+
#### Addon Options
|
|
39
|
+
|
|
40
|
+
You can configure which toolsets are enabled by default in your `.storybook/main.js`:
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
// .storybook/main.js
|
|
44
|
+
export default {
|
|
45
|
+
addons: [
|
|
46
|
+
{
|
|
47
|
+
name: '@storybook/addon-mcp',
|
|
48
|
+
options: {
|
|
49
|
+
toolsets: {
|
|
50
|
+
dev: true, // Tools for story URL retrieval and UI building instructions (default: true)
|
|
51
|
+
docs: true, // Tools for component manifest and documentation (default: true, requires experimental feature)
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Available Toolsets:**
|
|
60
|
+
|
|
61
|
+
- `dev`: Enables [Dev Tools](#dev-tools)
|
|
62
|
+
- `docs`: Enables [Documentation Tools](#docs-tools-experimental)
|
|
63
|
+
|
|
64
|
+
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.
|
|
65
|
+
|
|
66
|
+
#### Configuring toolsets with headers
|
|
67
|
+
|
|
68
|
+
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):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"storybook-mcp": {
|
|
73
|
+
"url": "http://localhost:6006/mcp",
|
|
74
|
+
"type": "http",
|
|
75
|
+
"headers": {
|
|
76
|
+
"X-MCP-Toolsets": "docs"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
36
82
|
### Configuring Your Agent
|
|
37
83
|
|
|
38
84
|
> [!NOTE]
|
|
@@ -88,7 +134,7 @@ This addon provides MCP tools that your agent can use. The goal is that the agen
|
|
|
88
134
|
|
|
89
135
|
**If you are prompting from an IDE like VSCode or Cursor, be sure to use `Agent` mode and `sonnet-4.5` or better.**
|
|
90
136
|
|
|
91
|
-
###
|
|
137
|
+
### Dev Tools
|
|
92
138
|
|
|
93
139
|
These tools are always available when the addon is installed:
|
|
94
140
|
|
|
@@ -119,12 +165,13 @@ Agent calls tool, gets response:
|
|
|
119
165
|
http://localhost:6006/?path=/story/example-button--primary
|
|
120
166
|
```
|
|
121
167
|
|
|
122
|
-
###
|
|
168
|
+
### Docs Tools (Experimental)
|
|
123
169
|
|
|
124
170
|
These additional tools are available when the **experimental** component manifest feature is enabled. They provide agents with detailed documentation about your UI components.
|
|
125
171
|
|
|
126
172
|
**Requirements:**
|
|
127
173
|
|
|
174
|
+
- Storybook version 10.1.0 or higher (currently only available as prereleases, `storybook@next`)
|
|
128
175
|
- React-based framework (`react-vite`, `nextjs-vite`, `nextjs`, `react-webpack5`)
|
|
129
176
|
- Feature flag `features.experimentalComponentsManifest` set to `true` in `.storybook/main.js`
|
|
130
177
|
|
package/dist/preset.js
CHANGED
|
@@ -11,7 +11,7 @@ import { buffer } from "node:stream/consumers";
|
|
|
11
11
|
|
|
12
12
|
//#region package.json
|
|
13
13
|
var name = "@storybook/addon-mcp";
|
|
14
|
-
var version = "0.1.
|
|
14
|
+
var version = "0.1.2";
|
|
15
15
|
var description = "Help agents automatically write and test stories for your UI components";
|
|
16
16
|
|
|
17
17
|
//#endregion
|
|
@@ -69,6 +69,13 @@ const errorToMCPContent = (error) => {
|
|
|
69
69
|
|
|
70
70
|
//#endregion
|
|
71
71
|
//#region src/types.ts
|
|
72
|
+
const AddonOptions = v.object({ toolsets: v.optional(v.object({
|
|
73
|
+
dev: v.exactOptional(v.boolean(), true),
|
|
74
|
+
docs: v.exactOptional(v.boolean(), true)
|
|
75
|
+
}), {
|
|
76
|
+
dev: true,
|
|
77
|
+
docs: true
|
|
78
|
+
}) });
|
|
72
79
|
/**
|
|
73
80
|
* Schema for a single story input when requesting story URLs.
|
|
74
81
|
*/
|
|
@@ -95,7 +102,8 @@ async function addGetStoryUrlsTool(server) {
|
|
|
95
102
|
name: GET_STORY_URLS_TOOL_NAME,
|
|
96
103
|
title: "Get stories' URLs",
|
|
97
104
|
description: `Get the URL for one or more stories.`,
|
|
98
|
-
schema: GetStoryUrlsInput
|
|
105
|
+
schema: GetStoryUrlsInput,
|
|
106
|
+
enabled: () => server.ctx.custom?.toolsets?.dev ?? true
|
|
99
107
|
}, async (input) => {
|
|
100
108
|
try {
|
|
101
109
|
const { origin: origin$1, disableTelemetry } = server.ctx.custom ?? {};
|
|
@@ -155,7 +163,8 @@ async function addGetUIBuildingInstructionsTool(server) {
|
|
|
155
163
|
description: `Instructions on how to do UI component development.
|
|
156
164
|
|
|
157
165
|
ALWAYS call this tool before doing any UI/frontend/React/component development, including but not
|
|
158
|
-
limited to adding or updating new components, pages, screens or layouts
|
|
166
|
+
limited to adding or updating new components, pages, screens or layouts.`,
|
|
167
|
+
enabled: () => server.ctx.custom?.toolsets?.dev ?? true
|
|
159
168
|
}, async () => {
|
|
160
169
|
try {
|
|
161
170
|
const { options, disableTelemetry } = server.ctx.custom ?? {};
|
|
@@ -217,27 +226,44 @@ const initializeMCPServer = async (options) => {
|
|
|
217
226
|
const [features, componentManifestGenerator] = await Promise.all([options.presets.apply("features"), options.presets.apply("experimental_componentManifestGenerator")]);
|
|
218
227
|
if (features.experimentalComponentsManifest && componentManifestGenerator) {
|
|
219
228
|
logger.info("Experimental components manifest feature detected - registering component tools");
|
|
220
|
-
|
|
221
|
-
await
|
|
229
|
+
const contextAwareEnabled = () => server.ctx.custom?.toolsets?.docs ?? true;
|
|
230
|
+
await addListAllComponentsTool(server, contextAwareEnabled);
|
|
231
|
+
await addGetComponentDocumentationTool(server, contextAwareEnabled);
|
|
222
232
|
}
|
|
223
233
|
transport = new HttpTransport(server, { path: null });
|
|
224
234
|
origin = `http://localhost:${options.port}`;
|
|
225
235
|
logger.debug("MCP server origin:", origin);
|
|
236
|
+
return server;
|
|
226
237
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
* This converts Node.js IncomingMessage/ServerResponse to Web API Request/Response.
|
|
230
|
-
*/
|
|
231
|
-
const mcpServerHandler = async (req, res, next, options) => {
|
|
232
|
-
const { disableTelemetry = false } = await options.presets.apply("core", {});
|
|
238
|
+
const mcpServerHandler = async ({ req, res, next, options, addonOptions }) => {
|
|
239
|
+
const disableTelemetry = options.disableTelemetry ?? false;
|
|
233
240
|
if (!initialize) initialize = initializeMCPServer(options);
|
|
234
|
-
await initialize;
|
|
241
|
+
const server = await initialize;
|
|
235
242
|
const webRequest = await incomingMessageToWebRequest(req);
|
|
236
243
|
const addonContext = {
|
|
237
244
|
options,
|
|
245
|
+
toolsets: getToolsets(webRequest, addonOptions),
|
|
238
246
|
origin,
|
|
239
247
|
disableTelemetry,
|
|
240
|
-
source: `${origin}/manifests/components.json
|
|
248
|
+
source: `${origin}/manifests/components.json`,
|
|
249
|
+
...!disableTelemetry && {
|
|
250
|
+
onListAllComponents: async ({ manifest }) => {
|
|
251
|
+
await collectTelemetry({
|
|
252
|
+
event: "tool:listAllComponents",
|
|
253
|
+
server,
|
|
254
|
+
componentCount: Object.keys(manifest.components).length
|
|
255
|
+
});
|
|
256
|
+
},
|
|
257
|
+
onGetComponentDocumentation: async ({ input, foundComponents, notFoundIds }) => {
|
|
258
|
+
await collectTelemetry({
|
|
259
|
+
event: "tool:getComponentDocumentation",
|
|
260
|
+
server,
|
|
261
|
+
inputComponentCount: input.componentIds.length,
|
|
262
|
+
foundCount: foundComponents.length,
|
|
263
|
+
notFoundCount: notFoundIds.length
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
241
267
|
};
|
|
242
268
|
const response = await transport.respond(webRequest, addonContext);
|
|
243
269
|
if (response) await webResponseToServerResponse(response, res);
|
|
@@ -278,11 +304,32 @@ async function webResponseToServerResponse(webResponse, nodeResponse) {
|
|
|
278
304
|
}
|
|
279
305
|
nodeResponse.end();
|
|
280
306
|
}
|
|
307
|
+
function getToolsets(request, addonOptions) {
|
|
308
|
+
const toolsetHeader = request.headers.get("X-MCP-Toolsets");
|
|
309
|
+
if (!toolsetHeader || toolsetHeader.trim() === "") return addonOptions.toolsets;
|
|
310
|
+
const toolsets = {
|
|
311
|
+
dev: false,
|
|
312
|
+
docs: false
|
|
313
|
+
};
|
|
314
|
+
const enabledToolsets = toolsetHeader.split(",");
|
|
315
|
+
for (const enabledToolset of enabledToolsets) {
|
|
316
|
+
const trimmedToolset = enabledToolset.trim();
|
|
317
|
+
if (trimmedToolset in toolsets) toolsets[trimmedToolset] = true;
|
|
318
|
+
}
|
|
319
|
+
return toolsets;
|
|
320
|
+
}
|
|
281
321
|
|
|
282
322
|
//#endregion
|
|
283
323
|
//#region src/preset.ts
|
|
284
324
|
const experimental_devServer = (app, options) => {
|
|
285
|
-
|
|
325
|
+
const addonOptions = v.parse(AddonOptions, { toolsets: options.toolsets ?? {} });
|
|
326
|
+
app.use("/mcp", (req, res, next) => mcpServerHandler({
|
|
327
|
+
req,
|
|
328
|
+
res,
|
|
329
|
+
next,
|
|
330
|
+
options,
|
|
331
|
+
addonOptions
|
|
332
|
+
}));
|
|
286
333
|
return app;
|
|
287
334
|
};
|
|
288
335
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/addon-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Help agents automatically write and test stories for your UI components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"storybook-addon",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@tmcp/adapter-valibot": "^0.1.4",
|
|
30
|
-
"@tmcp/transport-http": "^0.
|
|
31
|
-
"tmcp": "^1.
|
|
30
|
+
"@tmcp/transport-http": "^0.8.0",
|
|
31
|
+
"tmcp": "^1.16.0",
|
|
32
32
|
"valibot": "^1.1.0",
|
|
33
|
-
"@storybook/mcp": "0.0.
|
|
33
|
+
"@storybook/mcp": "0.0.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "20.19.0",
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
"tsdown": "^0.15.9",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
42
|
"vite": "^7.0.5",
|
|
43
|
-
"vitest": "
|
|
43
|
+
"vitest": "3.2.4"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"storybook": "
|
|
46
|
+
"storybook": "^9.1.16 || ^10.0.0"
|
|
47
47
|
},
|
|
48
48
|
"publishConfig": {
|
|
49
49
|
"access": "public"
|