Skip to content

Commit 8b66aeb

Browse files
authored
Merge pull request #11 from storybookjs/improve-instructions
Improve UI Building instructions
2 parents bdda77a + bba9b8c commit 8b66aeb

File tree

11 files changed

+173
-51
lines changed

11 files changed

+173
-51
lines changed

.changeset/loose-cobras-cheat.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@storybook/addon-mcp": patch
3+
---
4+
5+
- Improved UI Building Instructions
6+
- Improved output format of Get Story URLs tool

.mcp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
"url": "http://localhost:6006/mcp"
66
}
77
}
8-
}
8+
}

.storybook/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const config = defineMain({
1313
addons: ["@storybook/addon-docs", import.meta.resolve("../dist/preset.js")],
1414
framework: "@storybook/react-vite",
1515
logLevel: "debug",
16+
core: {
17+
disableTelemetry: true,
18+
},
1619
});
1720

1821
export default config;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"build-storybook": "storybook build",
3131
"build:watch": "pnpm run build -- --watch",
3232
"changeset": "changeset",
33-
"inspect": "mcp-inspector --config .mcp.json",
33+
"inspect": "mcp-inspector --transport streamable-http --server-url http://localhost:6006/mcp",
3434
"release": "pnpm run build && pnpm changeset publish",
3535
"start": "run-p build:watch \"storybook --quiet\"",
3636
"storybook": "storybook dev --port 6006 --loglevel debug",

src/mcp-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async function createMcpServer(options: Options, client: string) {
5252
});
5353

5454
registerStoryUrlsTool({ server, options });
55-
registerUIBuildingTool(server);
55+
registerUIBuildingTool({ server, options });
5656

5757
server.connect(transport);
5858
return transport;

src/tools/get-story-urls.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,10 @@ export function registerStoryUrlsTool({
9696
});
9797

9898
return {
99-
content: [
100-
{
101-
type: "text",
102-
text: result.length > 1 ? `- ${result.join("\n- ")}` : result[0]!,
103-
},
104-
],
99+
content: result.map((text) => ({
100+
type: "text",
101+
text,
102+
})),
105103
// Note: Claude Code seems to ignore structuredContent at the moment https://github.com/anthropics/claude-code/issues/4427
106104
structuredContent: { urls: result },
107105
};
Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,17 @@
11
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { GET_STORY_URLS_TOOL_NAME } from "./get-story-urls";
33
import { collectTelemetry } from "../telemetry";
4+
import uiInstructionsTemplate from "../ui-building-instructions.md";
5+
import type { Options } from "storybook/internal/types";
6+
import { logger } from "storybook/internal/node-logger";
47

5-
const INSTRUCTIONS = `
6-
# Writing User Interfaces Components
7-
8-
When writing UI components, prefer breaking larger components up into smaller parts.
9-
10-
ALWAYS write a Storybook story for any component written. If editing a component, ensure appropriate changes have been made to stories for that component.
11-
12-
When writing stories, use CSF3 importing types from '@storybook/nextjs-vite'. Here is a simple example:
13-
14-
\`\`\`ts
15-
import { Meta, StoryObj } from '@storybook/nextjs-vite';
16-
17-
import { Break } from './Break';
18-
19-
type Story = StoryObj<typeof Break>;
20-
21-
const meta: Meta<typeof Break> = {
22-
component: Break,
23-
args: {},
24-
};
25-
26-
export default meta;
27-
28-
export const Horizontal: Story = {
29-
args: {
30-
orientation: 'horizontal',
31-
},
32-
};
33-
\`\`\`
34-
35-
ALWAYS provide story links after any changes to stories files, including changes to existing stories.
36-
After changing any UI components, ALWAYS search for related stories that might cover the changes you've made.
37-
If you find any, provide the story links to the user. THIS IS VERY IMPORTANT, as it allows the user
38-
to visually inspect the modifications you've made.
39-
Even later in a session when changing UI components or stories that have already been linked to previously, YOU MUST PROVIDE THE LINKS AGAIN.
40-
Use the ${GET_STORY_URLS_TOOL_NAME} tool /and provide links in the format \`[Story Name](<story_url_from_tool>)\`
41-
whenever you create, modify, or update any story file.
42-
`;
43-
44-
export function registerUIBuildingTool(server: McpServer) {
8+
export function registerUIBuildingTool({
9+
server,
10+
options,
11+
}: {
12+
server: McpServer;
13+
options: Options;
14+
}) {
4515
server.registerTool(
4616
"get_ui_building_instructions",
4717
{
@@ -57,9 +27,53 @@ export function registerUIBuildingTool(server: McpServer) {
5727
event: "tool:getUIBuildingInstructions",
5828
mcpSessionId: sessionId!,
5929
});
30+
31+
const frameworkPreset = await options.presets.apply("framework");
32+
const framework =
33+
typeof frameworkPreset === "string"
34+
? frameworkPreset
35+
: frameworkPreset?.name;
36+
const renderer = frameworkToRendererMap[framework!];
37+
38+
if (!framework || !renderer) {
39+
logger.debug("Unable to determine framework or renderer", {
40+
frameworkPreset,
41+
framework,
42+
renderer,
43+
});
44+
}
45+
46+
const uiInstructions = uiInstructionsTemplate
47+
.replace("{{FRAMEWORK}}", framework)
48+
.replace("{{RENDERER}}", renderer ?? framework)
49+
.replace("{{GET_STORY_URLS_TOOL_NAME}}", GET_STORY_URLS_TOOL_NAME);
50+
6051
return {
61-
content: [{ type: "text", text: INSTRUCTIONS }],
52+
content: [{ type: "text", text: uiInstructions }],
6253
};
6354
},
6455
);
6556
}
57+
58+
// TODO: this is a stupid map to maintain and it's not complete, but we can't easily get the current renderer name
59+
const frameworkToRendererMap: Record<string, string> = {
60+
"@storybook/react-vite": "@storybook/react",
61+
"@storybook/react-webpack5": "@storybook/react",
62+
"@storybook/nextjs": "@storybook/react",
63+
"@storybook/nextjs-vite": "@storybook/react",
64+
"@storybook/react-native-web-vite": "@storybook/react",
65+
66+
"@storybook/vue3-vite": "@storybook/vue3",
67+
"@nuxtjs/storybook": "@storybook/vue3",
68+
69+
"@storybook/angular": "@storybook/angular",
70+
71+
"@storybook/svelte-vite": "@storybook/svelte",
72+
"@storybook/sveltekit": "@storybook/svelte",
73+
74+
"@storybook/preact-vite": "@storybook/preact",
75+
76+
"@storybook/web-components-vite": "@storybook/web-components",
77+
78+
"@storybook/html-vite": "@storybook/html",
79+
};

src/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare module "*.md" {
2+
const content: string;
3+
export default content;
4+
}

src/ui-building-instructions.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Storybook 9 Essential Changes for Story Writing
2+
3+
## Package Consolidation
4+
5+
### `Meta` and `StoryObj` imports
6+
7+
Update story imports to use the framework package:
8+
9+
```diff
10+
- import { Meta, StoryObj } from '{{RENDERER}}';
11+
+ import { Meta, StoryObj } from '{{FRAMEWORK}}';
12+
```
13+
14+
### Test utility imports
15+
16+
Update test imports to use `storybook/test` instead of `@storybook/test`
17+
18+
```diff
19+
- import { fn } from '@storybook/test';
20+
+ import { fn } from 'storybook/test';
21+
```
22+
23+
## Global State Changes
24+
25+
The `globals` annotation has be renamed to `initialGlobals`:
26+
27+
```diff
28+
// .storybook/preview.js
29+
export default {
30+
- globals: { theme: 'light' }
31+
+ initialGlobals: { theme: 'light' }
32+
};
33+
```
34+
35+
## Autodocs Configuration
36+
37+
Instead of `parameters.docs.autodocs` in main.js, use tags:
38+
39+
```js
40+
// .storybook/preview.js or in individual stories
41+
export default {
42+
tags: ["autodocs"], // generates autodocs for all stories
43+
};
44+
```
45+
46+
## Mocking imports in Storybook
47+
48+
To mock imports in Storybook, use Storybook's mocking features. ALWAYS mock external dependencies to ensure stories render consistently.
49+
50+
1. **Register in the mock in Storybook's preview file**:
51+
To mock dependendencies, you MUST register a module mock in `.storybook/preview.ts` (or equivalent):
52+
53+
```js
54+
import { sb } from "storybook/test";
55+
56+
// Full mock (replaces with empty fn functions)
57+
sb.mock(import("some-library"));
58+
```
59+
60+
2. **Specify mock values in stories**:
61+
You can override the behaviour of the mocks per-story using `beforeEach` and the `mocked()` type function:
62+
63+
```js
64+
import { expect, mocked, fn } from 'storybook/test';
65+
import { library } from 'some-library';
66+
67+
const meta = {
68+
component: AuthButton,
69+
beforeEach: async () => {
70+
mocked(library).mockResolvedValue({ user: 'data' });
71+
},
72+
};
73+
74+
export const LoggedIn: Story = {
75+
play: async ({ canvas }) => {
76+
await expect(library).toHaveBeenCalled();
77+
},
78+
};
79+
```
80+
81+
Before doing this ensure you have mocked the import in the preview file.
82+
83+
## Key Requirements
84+
85+
- **Node.js 20+**, **TypeScript 4.9+**, **Vite 5+**
86+
- React Native uses `.rnstorybook` directory
87+
- `afterEach` hook is now stable (was `experimental_afterEach`)
88+
89+
# Story Linking Agent Behavior
90+
91+
- ALWAYS provide story links after any changes to stories files, including changes to existing stories.
92+
- After changing any UI components, ALWAYS search for related stories that might cover the changes you've made. If you find any, provide the story links to the user. THIS IS VERY IMPORTANT, as it allows the user to visually inspect the changes you've made. Even later in a session when changing UI components or stories that have already been linked to previously, YOU MUST PROVIDE THE LINKS AGAIN.
93+
- Use the {{GET_STORY_URLS_TOOL_NAME}} tool to get the correct URLs for links to stories.

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"noImplicitAny": true,
1818
"lib": ["esnext", "dom", "dom.iterable"],
1919
"baseUrl": ".",
20-
"rootDir": "./src"
20+
"rootDir": "./src",
21+
"types": ["./src/types.d.ts"]
2122
},
2223
"include": ["src/**/*", "tsup.config.ts"]
2324
}

0 commit comments

Comments
 (0)