
Storybook MCP按 Story ID 预览既有 Story 且不改动源码的完整工作流解析【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本文以 Storybook 仓库中agent-eval/evals/915-preview-story-by-id评测用例其任务提示见 PROMPT.md为核心骨架展开。该用例要求 AI Agent 对既有的Button组件的Primary与Secondary两个 Story 生成预览链接且不得修改任何组件或 Story 文件。读完后你能掌握如何用 Storybook 的 MCP /stories-preview工作流按 Story ID 精确预览既有 Story、Story ID 的命名规则如何从title与导出名推导、评测断言如何校验「按 ID 预览」这一行为以及预览工具在仓库中的底层实现。一、任务背景一个「只读预览」评测场景915-preview-story-by-id是仓库 agent-eval 目录下的一个评测用例其 PROMPT.md 原文只有两句话却完整定义了一个典型任务边界Show a preview of two existing Button stories: Primary and Secondary. Do not modify any component or story files.这两句浓缩了三个关键约束对象是「既有的 Story」——不是新建组件、不是改 Story目标 Story 已经存在于仓库中。目标是「生成预览」——把 Story 渲染出来并以可访问的链接形式交付给调用方。行为红线是「只读」——不得改动任何组件或 Story 文件任何写盘操作都算失败。这类任务在 Storybook 面向 AI Agent 的场景中很有代表性Agent 常常需要「看一眼某个组件现在长什么样」而不是「改它」。因此正确做法是调用预览类工作流而非触发组件创建/编辑类工作流。该用例在 package.json 中声明了所使用的沙箱模板{ name: 915-preview-story-by-id, type: module, evals: { template: reshaped-storybook } }template: reshaped-storybook指向 agent-eval/templates/reshaped-storybook 模板评测会在该模板生成的沙箱项目中运行 Agent再由断言脚本检验其调用轨迹。二、评测夹具中的组件与 Story理解 Story ID 从何而来需要先看夹具本身。组件 src/components/Button.tsx 是一个极简的受控按钮type ButtonProps { label: string; disabled?: boolean; }; export default function Button({ label, disabled false }: ButtonProps) { return ( button typebutton disabled{disabled}>import type { Meta, StoryObj } from storybook/react; import Button from ../src/components/Button; const meta { title: Example/Button, component: Button, tags: [test], args: { label: Click me, disabled: false, }, } satisfies Metatypeof Button; export default meta; type Story StoryObjtypeof meta; export const Primary: Story { args: { label: Primary, }, }; export const Secondary: Story { args: { label: Secondary, }, };从这份 Story 文件可以直接读出 Story ID 的构成规则title: Example/Button会被 kebab-case 化前缀为example-button导出名Primary转为--primarySecondary转为--secondary最终得到example-button--primary与example-button--secondary两个 Story ID。这套「title kebab-case -- 导出名 kebab-case」的拼接逻辑与评测工具 agent-eval/lib/test-utils.ts 中kebabCase辅助函数的实现保持一致把驼峰拆成连字符、转小写可据此交叉验证 ID 的推导是否正确。三、断言逻辑如何判定 Agent「按 ID 预览」成功EVAL.ts 是该用例的验收脚本用 vitest 编写全部断言围绕stories-preview这一个工作流import { describe, expect, test } from vitest; import { expectWorkflowCalls, getWorkflowCalls, workflowCallIncludesStory, workflowCallUsesStoryId, } from #test-utils; describe(previewing Button stories by story ID, () { test(previews stories using story IDs, () { const previewCalls getWorkflowCalls(stories-preview); expectWorkflowCalls([stories-preview]); expect(previewCalls.some(workflowCallUsesStoryId)).toBe(true); expect( previewCalls.some((call) workflowCallIncludesStory(call, { storyId: example-button--primary }) ) ).toBe(true); expect( previewCalls.some((call) workflowCallIncludesStory(call, { storyId: example-button--secondary }) ) ).toBe(true); }); });逐条解读这组断言可以清楚知道「及格线」在哪里expectWorkflowCalls([stories-preview])要求 Agent 至少调用过一次stories-preview工作流。这确保了 Agent 走的是「预览」路径而不是去创建/编辑组件。previewCalls.some(workflowCallUsesStoryId)要求至少有一次预览调用是通过storyId字段发起的。这正是用例标题「by id」的含义——不能只靠文件路径或导出名必须使用规范化的 Story ID。两个workflowCallIncludesStory(...)分别要求预览调用覆盖了example-button--primary和example-button--secondary即 Primary 与 Secondary 两个 Story 都必须被预览到缺一不可。这三个断言共同刻画了「按 Story ID 预览既有 Story」这一行为的完整判定正确的预览工作流 使用 storyId 入参 覆盖到任务指定的两个具体 Story。其中几个校验函数定义在共享工具 agent-eval/lib/test-utils.tsgetWorkflowCalls(name)第 122-124 行从 Agent 的调用轨迹中过滤出指定名称的工作流调用。workflowCallUsesStoryId第 801-803 行判断某次调用的 story 入参里是否存在字符串类型的storyId。workflowCallIncludesStory第 794-799 行判断某次调用是否覆盖了期望的 story 入参可含storyId/exportName/absoluteStoryPath。这些函数会解析 Agent 的 transcript__agent_eval__/transcript.txt与 shell 命令记录__agent_eval__/results.json兼容 MCP 工具调用与storybook aiCLI 两条路径见 test-utils.ts 中getStorybookWorkflowCalls对integration plugin与 MCP 的分支处理。四、底层实现preview-stories 工具如何产出预览评测断言校验的是 Agent 的调用轨迹而stories-preview工作流背后的真实执行逻辑位于 MCP 插件包中。code/addons/mcp/src/tools/preview-stories.ts 注册了一个供 MCP 客户端内联渲染 story 预览的应用资源export const PREVIEW_STORIES_RESOURCE_URI ui://${PREVIEW_STORIES_TOOL_NAME}/preview.html; /** * Serves the MCP app that renders story previews inline in the client. * * The app reads the tool results structuredContent, so it is bound to the preview tools output * contract rather than to its implementation. */ export async function addPreviewStoriesResource(server: McpServerany, AddonContext) { const previewStoryAppScript await fs.readFile( url.fileURLToPath( import.meta.resolve(storybook/addon-mcp/internal/preview-stories-app-script) ), utf-8 ); const appHtml appTemplate.replace(// APP_SCRIPT_PLACEHOLDER, previewStoryAppScript); server.resource( { name: PREVIEW_STORIES_RESOURCE_URI, description: App resource for the Preview Stories tool, uri: PREVIEW_STORIES_RESOURCE_URI, mimeType: text/html;profilemcp-app, }, () { const origin server.ctx.custom!.origin; return { contents: [ { uri: PREVIEW_STORIES_RESOURCE_URI, mimeType: text/html;profilemcp-app, text: appHtml, _meta: { ui: { prefersBorder: false, domain: origin, csp: { connectDomains: [origin], resourceDomains: [origin], frameDomains: [origin], baseUriDomains: [origin], }, }, }, }, ], }; } ); }从源码结构看该资源把预览页 HTML 模板 preview-stories-app-template.html 与应用脚本来自storybook/addon-mcp/internal/preview-stories-app-script拼接后作为text/html;profilemcp-app的 MCP 资源暴露出去并附带面向预览源站的 CSP 域名配置connectDomains/resourceDomains/frameDomains/baseUriDomains。注释也明确说明该应用读取工具结果的structuredContent即它绑定的是预览工具的输出契约而非其具体实现。这与评测断言「绑定输出而非实现」的校验思路一脉相承——断言只关心 Agent 是否以正确的 story 入参触发了预览、并交付了预览链接而不关心预览页内部如何渲染。配合 code/addons/mcp/README.md 与 code/addons/mcp/src/tools/tool-names.ts 中的工具命名常量可以确认stories-preview正是该插件对外暴露的「预览 Story」工作流入口。五、可操作要点与适用边界把上述证据串起来针对「按 Story ID 预览既有 Story」这一任务可提炼出以下可操作要点优先使用storyId入参当已知目标 Story 的规范 ID如example-button--primary时应以storyId作为stories-preview的 story 入参而不是仅传文件路径或导出名。评测中workflowCallUsesStoryId断言专门校验了这一点。ID 推导要可复现Story ID 由title的 kebab-case 形式拼接--与导出名的 kebab-case 形式得到。修改title或导出名会直接改变 ID因此预览前应核对 Story 文件的title与export const名称。严守「只读」边界任务明确禁止改动组件或 Story 文件时正确行为是只调用预览工作流。任何写盘/编辑类工作流都不应出现。预览结果以链接交付从 test-utils.ts 的STORYBOOK_PREVIEW_URL_PATTERN第 517 行/[?]path\/|\/iframe\.html\?/可推断预览链接的形态是 manager 页的?path/story/…或 iframe 的/iframe.html?id…两者都以 Story ID 为核心定位参数。适用前提以上行为基于当前仓库中915-preview-story-by-id评测夹具与storybook/addon-mcp的实现storyId的 kebab-case 拼接规则与stories-preview工作流入口以本仓库对应文件为准。若目标项目使用了不同的title约定或自定义 Story ID 生成逻辑应以项目实际的 Story 文件与 Storybook 版本为准。六、参考文件任务提示agent-eval/evals/915-preview-story-by-id/PROMPT.md评测断言agent-eval/evals/915-preview-story-by-id/EVAL.ts用例配置agent-eval/evals/915-preview-story-by-id/package.json夹具组件agent-eval/evals/915-preview-story-by-id/src/components/Button.tsx夹具 Storyagent-eval/evals/915-preview-story-by-id/stories/Button.stories.tsx评测共享工具agent-eval/lib/test-utils.ts预览工具实现code/addons/mcp/src/tools/preview-stories.ts预览页模板code/addons/mcp/src/tools/preview-stories/preview-stories-app-template.htmlMCP 插件说明code/addons/mcp/README.md【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考