@storybook/addon-mcp 0.3.3 → 0.4.0
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 +14 -9
- package/dist/preset.js +46 -16
- package/dist/preview-stories-app-script.js +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -48,21 +48,19 @@ export default {
|
|
|
48
48
|
options: {
|
|
49
49
|
toolsets: {
|
|
50
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
|
|
51
|
+
docs: true, // Tools for component manifest and documentation (default: true)
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
|
-
|
|
57
|
-
experimentalComponentsManifest: true, // Enable manifest generation for the docs toolset, only supported in React-based setups.
|
|
58
|
-
},
|
|
56
|
+
// componentsManifest is enabled by default in recent Storybook versions, no need to set it
|
|
59
57
|
};
|
|
60
58
|
```
|
|
61
59
|
|
|
62
60
|
**Available Toolsets:**
|
|
63
61
|
|
|
64
62
|
- `dev`: Enables [Dev Tools](#dev-tools)
|
|
65
|
-
- `docs`: Enables [Documentation Tools](#docs-tools
|
|
63
|
+
- `docs`: Enables [Documentation Tools](#docs-tools)
|
|
66
64
|
|
|
67
65
|
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.
|
|
68
66
|
|
|
@@ -172,24 +170,31 @@ Agent calls tool, gets response:
|
|
|
172
170
|
http://localhost:6006/?path=/story/example-button--primary
|
|
173
171
|
```
|
|
174
172
|
|
|
175
|
-
### Docs Tools
|
|
173
|
+
### Docs Tools
|
|
176
174
|
|
|
177
|
-
These additional tools are available when the
|
|
175
|
+
These additional tools are available when the component manifest feature is enabled. They provide agents with detailed documentation about your UI components.
|
|
178
176
|
|
|
179
177
|
**Requirements:**
|
|
180
178
|
|
|
181
179
|
- Storybook version 10.1.0 or higher (currently only available as prereleases, `storybook@next`)
|
|
182
180
|
- React-based framework (`react-vite`, `nextjs-vite`, `nextjs`, `react-webpack5`)
|
|
183
|
-
- Feature flag `features.
|
|
181
|
+
- Feature flag `features.componentsManifest` enabled (defaults to `true` in recent Storybook versions)
|
|
184
182
|
|
|
185
183
|
**To enable:**
|
|
186
184
|
|
|
185
|
+
The `componentsManifest` feature is enabled by default in recent Storybook versions — no configuration needed.
|
|
186
|
+
|
|
187
|
+
If you are on an older Storybook version that doesn't default to `true`, you may need to enable it explicitly. Use the flag that matches your Storybook version:
|
|
188
|
+
|
|
187
189
|
```javascript
|
|
188
190
|
// .storybook/main.js
|
|
189
191
|
export default {
|
|
190
192
|
// ... other config
|
|
191
193
|
features: {
|
|
192
|
-
|
|
194
|
+
// For Storybook 10.3.x and later:
|
|
195
|
+
componentsManifest: true,
|
|
196
|
+
// For older Storybook versions (before the flag was renamed):
|
|
197
|
+
// experimentalComponentsManifest: true,
|
|
193
198
|
},
|
|
194
199
|
};
|
|
195
200
|
```
|
package/dist/preset.js
CHANGED
|
@@ -9,13 +9,13 @@ import { stringify } from "picoquery";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { normalizeStoryPath } from "storybook/internal/common";
|
|
11
11
|
import { storyNameFromExport } from "storybook/internal/csf";
|
|
12
|
-
import { ComponentManifestMap, DocsManifestMap, GET_TOOL_NAME, LIST_TOOL_NAME, addGetDocumentationTool, addGetStoryDocumentationTool, addListAllDocumentationTool } from "@storybook/mcp";
|
|
12
|
+
import { ComponentManifestMap, DocsManifestMap, GET_TOOL_NAME, LIST_TOOL_NAME, STORYBOOK_MCP_INSTRUCTIONS, addGetDocumentationTool, addGetStoryDocumentationTool, addListAllDocumentationTool } from "@storybook/mcp";
|
|
13
13
|
import fs from "node:fs/promises";
|
|
14
14
|
import { buffer } from "node:stream/consumers";
|
|
15
15
|
|
|
16
16
|
//#region package.json
|
|
17
17
|
var name = "@storybook/addon-mcp";
|
|
18
|
-
var version = "0.
|
|
18
|
+
var version = "0.4.0";
|
|
19
19
|
var description = "Help agents automatically write and test stories for your UI components";
|
|
20
20
|
|
|
21
21
|
//#endregion
|
|
@@ -768,8 +768,8 @@ const getManifestStatus = async (options) => {
|
|
|
768
768
|
options.presets.apply("experimental_manifests", void 0, { manifestEntries: [] }),
|
|
769
769
|
options.presets.apply("experimental_componentManifestGenerator")
|
|
770
770
|
]);
|
|
771
|
-
const hasManifests =
|
|
772
|
-
const hasFeatureFlag = !!features?.experimentalComponentsManifest;
|
|
771
|
+
const hasManifests = manifests && "components" in manifests || !!legacyComponentManifestGenerator;
|
|
772
|
+
const hasFeatureFlag = !!(features?.componentsManifest ?? features?.experimentalComponentsManifest);
|
|
773
773
|
return {
|
|
774
774
|
available: hasFeatureFlag && hasManifests,
|
|
775
775
|
hasManifests,
|
|
@@ -831,6 +831,25 @@ function estimateTokens(text) {
|
|
|
831
831
|
return tokenCount;
|
|
832
832
|
}
|
|
833
833
|
|
|
834
|
+
//#endregion
|
|
835
|
+
//#region src/instructions/dev-instructions.md
|
|
836
|
+
var dev_instructions_default = "## UI Building and Story Writing Workflow\n\n- Before creating or editing components or stories, call **get-storybook-story-instructions**.\n- Treat that tool's output as the source of truth for framework-specific imports, story patterns, and testing conventions.\n- After changing any component or story, call **preview-stories**.\n- Always include every returned preview URL in your user-facing response so the user can verify the visual result.\n";
|
|
837
|
+
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/instructions/test-instructions.md
|
|
840
|
+
var test_instructions_default = "## Validation Workflow\n\n- After each component or story change, run **run-story-tests**.\n- Use focused runs while iterating, then run a broad pass before final handoff when scope is unclear or wide.\n- Fix failing tests before reporting success. Do not report completion while story tests are failing.\n";
|
|
841
|
+
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/instructions/build-server-instructions.ts
|
|
844
|
+
function buildServerInstructions(options) {
|
|
845
|
+
const sections = ["Follow these workflows when working with UI and/or Storybook."];
|
|
846
|
+
if (options.devEnabled) sections.push(dev_instructions_default.trim());
|
|
847
|
+
if (options.testEnabled) sections.push(test_instructions_default.trim());
|
|
848
|
+
if (options.docsEnabled) sections.push(STORYBOOK_MCP_INSTRUCTIONS.trim());
|
|
849
|
+
if (sections.length === 1) return "";
|
|
850
|
+
return sections.join("\n\n");
|
|
851
|
+
}
|
|
852
|
+
|
|
834
853
|
//#endregion
|
|
835
854
|
//#region src/mcp-handler.ts
|
|
836
855
|
let transport;
|
|
@@ -840,17 +859,29 @@ let disableTelemetry;
|
|
|
840
859
|
let a11yEnabled;
|
|
841
860
|
const initializeMCPServer = async (options, multiSource) => {
|
|
842
861
|
disableTelemetry = (await options.presets.apply("core", {}))?.disableTelemetry ?? false;
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
862
|
+
const addonVitestConstants = await getAddonVitestConstants();
|
|
863
|
+
const manifestStatus = await getManifestStatus(options);
|
|
864
|
+
a11yEnabled = await isAddonA11yEnabled(options);
|
|
865
|
+
let server;
|
|
866
|
+
const serverOptions = {
|
|
848
867
|
adapter: new ValibotJsonSchemaAdapter(),
|
|
868
|
+
get instructions() {
|
|
869
|
+
return buildServerInstructions({
|
|
870
|
+
devEnabled: server?.ctx.custom?.toolsets?.dev ?? true,
|
|
871
|
+
testEnabled: (server?.ctx.custom?.toolsets?.test ?? true) && !!addonVitestConstants,
|
|
872
|
+
docsEnabled: (server?.ctx.custom?.toolsets?.docs ?? true) && manifestStatus.available
|
|
873
|
+
});
|
|
874
|
+
},
|
|
849
875
|
capabilities: {
|
|
850
876
|
tools: { listChanged: true },
|
|
851
877
|
resources: { listChanged: true }
|
|
852
878
|
}
|
|
853
|
-
}
|
|
879
|
+
};
|
|
880
|
+
server = new McpServer({
|
|
881
|
+
name,
|
|
882
|
+
version,
|
|
883
|
+
description
|
|
884
|
+
}, serverOptions).withContext();
|
|
854
885
|
if (!disableTelemetry) server.on("initialize", async () => {
|
|
855
886
|
await collectTelemetry({
|
|
856
887
|
event: "session:initialized",
|
|
@@ -859,9 +890,8 @@ const initializeMCPServer = async (options, multiSource) => {
|
|
|
859
890
|
});
|
|
860
891
|
await addPreviewStoriesTool(server);
|
|
861
892
|
await addGetUIBuildingInstructionsTool(server);
|
|
862
|
-
a11yEnabled = await isAddonA11yEnabled(options);
|
|
863
893
|
await addRunStoryTestsTool(server, { a11yEnabled });
|
|
864
|
-
if (
|
|
894
|
+
if (manifestStatus.available) {
|
|
865
895
|
logger.info("Experimental components manifest feature detected - registering component tools");
|
|
866
896
|
const contextAwareEnabled = () => server.ctx.custom?.toolsets?.docs ?? true;
|
|
867
897
|
await addListAllDocumentationTool(server, contextAwareEnabled);
|
|
@@ -979,7 +1009,7 @@ function getToolsets(request, addonOptions) {
|
|
|
979
1009
|
|
|
980
1010
|
//#endregion
|
|
981
1011
|
//#region src/template.html
|
|
982
|
-
var template_default = "<!doctype html>\n<html>\n <head>\n
|
|
1012
|
+
var template_default = "<!doctype html>\n<html>\n <head>\n <style>\n @font-face {\n font-family: 'Nunito Sans';\n font-style: normal;\n font-weight: 400;\n font-display: swap;\n src: url('./sb-common-assets/nunito-sans-regular.woff2') format('woff2');\n }\n\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n html,\n body {\n height: 100%;\n font-family:\n 'Nunito Sans',\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n sans-serif;\n }\n\n body {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n text-align: center;\n padding: 2rem;\n background-color: #ffffff;\n color: rgb(46, 52, 56);\n line-height: 1.6;\n }\n\n p {\n margin-bottom: 1rem;\n }\n\n code {\n font-family: 'Monaco', 'Courier New', monospace;\n background: #f5f5f5;\n padding: 0.2em 0.4em;\n border-radius: 3px;\n }\n\n a {\n color: #1ea7fd;\n }\n\n .container {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n .toolsets {\n margin: 1.5rem 0;\n text-align: left;\n max-width: 500px;\n }\n\n .toolsets h3 {\n font-size: 1rem;\n margin-bottom: 0.75rem;\n text-align: center;\n }\n\n .toolset {\n margin-bottom: 1rem;\n padding: 0.75rem 1rem;\n border-radius: 6px;\n background: #f8f9fa;\n border: 1px solid #e9ecef;\n }\n\n .toolset-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n font-weight: 600;\n margin-bottom: 0.5rem;\n }\n\n .toolset-status {\n display: inline-block;\n padding: 0.15em 0.5em;\n border-radius: 3px;\n font-size: 0.75rem;\n font-weight: 500;\n text-transform: uppercase;\n }\n\n .toolset-status.enabled {\n background: #d4edda;\n color: #155724;\n }\n\n .toolset-status.disabled {\n background: #f8d7da;\n color: #721c24;\n }\n\n .toolset-tools {\n font-size: 0.875rem;\n color: #6c757d;\n padding-left: 1.5rem;\n margin: 0;\n }\n\n .toolset-tools li {\n margin-bottom: 0.25rem;\n }\n\n .toolset-tools code {\n font-size: 0.8rem;\n }\n\n .toolset-notice {\n font-size: 0.8rem;\n color: #856404;\n background: #fff3cd;\n padding: 0.5rem;\n border-radius: 4px;\n margin-top: 0.5rem;\n }\n\n .toolset-notice a {\n color: #533f03;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n background-color: rgb(34, 36, 37);\n color: rgb(201, 205, 207);\n }\n\n code {\n background: rgba(255, 255, 255, 0.1);\n }\n\n .toolset {\n background: rgba(255, 255, 255, 0.05);\n border-color: rgba(255, 255, 255, 0.1);\n }\n\n .toolset-tools {\n color: #adb5bd;\n }\n\n .toolset-status.enabled {\n background: rgba(40, 167, 69, 0.2);\n color: #75d67e;\n }\n\n .toolset-status.disabled {\n background: rgba(220, 53, 69, 0.2);\n color: #f5a6ad;\n }\n\n .toolset-notice {\n background: rgba(255, 193, 7, 0.15);\n color: #ffc107;\n }\n\n .toolset-notice a {\n color: #ffe066;\n }\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <p>\n Storybook MCP server successfully running via\n <code>@storybook/addon-mcp</code>.\n </p>\n <p>\n See how to connect to it from your coding agent in\n <a\n target=\"_blank\"\n href=\"https://github.com/storybookjs/mcp/tree/main/packages/addon-mcp#configuring-your-agent\"\n >the addon's README</a\n >.\n </p>\n\n <div class=\"toolsets\">\n <h3>Available Toolsets</h3>\n\n <div class=\"toolset\">\n <div class=\"toolset-header\">\n <span>dev</span>\n <span class=\"toolset-status {{DEV_STATUS}}\">{{DEV_STATUS}}</span>\n </div>\n <ul class=\"toolset-tools\">\n <li><code>preview-stories</code></li>\n <li><code>get-storybook-story-instructions</code></li>\n </ul>\n </div>\n\n <div class=\"toolset\">\n <div class=\"toolset-header\">\n <span>docs</span>\n <span class=\"toolset-status {{DOCS_STATUS}}\">{{DOCS_STATUS}}</span>\n </div>\n <ul class=\"toolset-tools\">\n <li><code>list-all-documentation</code></li>\n <li><code>get-documentation</code></li>\n </ul>\n {{DOCS_NOTICE}}\n </div>\n\n <div class=\"toolset\">\n <div class=\"toolset-header\">\n <span>test</span>\n <span class=\"toolset-status {{TEST_STATUS}}\">{{TEST_STATUS}}</span>\n </div>\n <ul class=\"toolset-tools\">\n <li><code>run-story-tests</code>{{A11Y_BADGE}}</li>\n </ul>\n {{TEST_NOTICE}}\n </div>\n </div>\n\n {{MANIFEST_DEBUGGER_LINK}}\n </div>\n </body>\n</html>\n";
|
|
983
1013
|
|
|
984
1014
|
//#endregion
|
|
985
1015
|
//#region src/auth/composition-auth.ts
|
|
@@ -1298,13 +1328,13 @@ const experimental_devServer = async (app, options) => {
|
|
|
1298
1328
|
This toolset is only supported in React-based setups.
|
|
1299
1329
|
</div>`;
|
|
1300
1330
|
else if (!manifestStatus.hasFeatureFlag) docsNotice = `<div class="toolset-notice">
|
|
1301
|
-
This toolset requires enabling the
|
|
1331
|
+
This toolset requires enabling the component manifest feature.
|
|
1302
1332
|
<a target="_blank" href="https://github.com/storybookjs/mcp/tree/main/packages/addon-mcp#docs-tools-experimental">Learn how to enable it</a>
|
|
1303
1333
|
</div>`;
|
|
1304
|
-
const testNoticeLines = [!addonVitestConstants && `This toolset requires <code>@storybook/addon-vitest</code>. <a target="_blank" href="https://storybook.js.org/docs/writing-tests/test-addon">Learn how to set it up</a>`, !a11yEnabled && `Add <code>@storybook/addon-a11y</code> for accessibility testing. <a target="_blank" href="https://storybook.js.org/docs/writing-tests/accessibility-testing">Learn more</a>`].filter(Boolean);
|
|
1334
|
+
const testNoticeLines = [!addonVitestConstants && `This toolset requires Storybook 10.3.0+ with <code>@storybook/addon-vitest</code>. <a target="_blank" href="https://storybook.js.org/docs/writing-tests/test-addon">Learn how to set it up</a>`, !a11yEnabled && `Add <code>@storybook/addon-a11y</code> for accessibility testing. <a target="_blank" href="https://storybook.js.org/docs/writing-tests/accessibility-testing">Learn more</a>`].filter(Boolean);
|
|
1305
1335
|
const testNotice = testNoticeLines.length ? `<div class="toolset-notice">${testNoticeLines.join("<br>")}</div>` : "";
|
|
1306
1336
|
const a11yBadge = a11yEnabled ? " <span class=\"toolset-status enabled\">+ accessibility</span>" : "";
|
|
1307
|
-
const html = template_default.
|
|
1337
|
+
const html = template_default.replaceAll("{{DEV_STATUS}}", isDevEnabled ? "enabled" : "disabled").replaceAll("{{DOCS_STATUS}}", isDocsEnabled ? "enabled" : "disabled").replace("{{DOCS_NOTICE}}", docsNotice).replaceAll("{{TEST_STATUS}}", isTestEnabled ? "enabled" : "disabled").replace("{{TEST_NOTICE}}", testNotice).replace("{{MANIFEST_DEBUGGER_LINK}}", manifestStatus.available ? "<p>View the <a href=\"/manifests/components.html\">component manifest debugger</a>.</p>" : "").replace("{{A11Y_BADGE}}", a11yBadge);
|
|
1308
1338
|
res.end(html);
|
|
1309
1339
|
});
|
|
1310
1340
|
return app;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/addon-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Help agents automatically write and test stories for your UI components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"picoquery": "^2.5.0",
|
|
35
35
|
"tmcp": "^1.16.0",
|
|
36
36
|
"valibot": "1.2.0",
|
|
37
|
-
"@storybook/mcp": "0.
|
|
37
|
+
"@storybook/mcp": "0.6.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@storybook/addon-a11y": "10.3.0-alpha.
|
|
41
|
-
"@storybook/addon-vitest": "10.3.0-alpha.
|
|
42
|
-
"storybook": "10.3.0-alpha.
|
|
40
|
+
"@storybook/addon-a11y": "10.3.0-alpha.16",
|
|
41
|
+
"@storybook/addon-vitest": "10.3.0-alpha.16",
|
|
42
|
+
"storybook": "10.3.0-alpha.16"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@storybook/addon-vitest": "^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0",
|