Inspector V2 组件开发指南:用 Mantine + Storybook 践行 Presentational Components 模式
开发工具MCP Clients调试器【免费下载链接】inspectorVisual testing tool for MCP servers项目地址https://gitcode.com/gh_mirrors/inspector1/inspector点击查看免费下载本文是 MCP InspectorMCP 服务器可视化测试工具V2 前端组件开发方法论的实战指南。核心主题是基于core/mcp/state状态层 core/reacthooks Mantine Storybook构建一套纯展示组件Presentational Components体系组件只通过 props 接收数据与回调不含任何业务逻辑从而获得独立的可测试性与可维护性。读完本文你将掌握这种以 props 接口为契约的组件设计纪律、Storybook 驱动的自底向上开发循环以及它们在当前仓库clients/web中的真实落地方式。一、背景Inspector V2 的前端技术栈与分层目标Inspector V2 的前端clients/web采用 React 19 Mantinemantine/core、mantine/form、mantine/notifications版本为^8.3.17见 package.json并搭配 Storybook 10 作为组件开发环境。更完整的组件清单与规格见 v2_ux_storybook.md整体 UX 设计见 v2_ux.md。整体分层的设计意图非常明确core/mcp/statestores持有由 InspectorClient 派生的数据如工具、资源、提示词列表是数据的唯一所有者容器组件或 hooks负责把 store 数据接线到展示组件展示组件Presentational Components纯渲染机器只依赖 props。对应到仓库状态层位于 core/mcp/state例如 managedToolsState.ts 封装了tools/list拉取、tools/list_changed变更通知与防抖React hooks 层位于 core/react如useManagedTools、usePagedTools展示组件则全部集中在 clients/web/src/components 下。二、Presentational / Container 分离组件即纯渲染机器文档中最核心的思想是一句话组件通过 props 接收它所渲染的全部数据以及用户在交互时触发的全部回调。组件本身既不知道数据从哪来也不关心按钮点击之后发生了什么——它只是调用onSave(formData)并信任上游有人处理。这种约定带来干净的分层core/mcp/state stores ── 容器组件 / hooks ── 展示组件纯渲染 数据所有者 接线层 props 契约从源码看这一点体现得非常彻底。例如 TransportBadge.tsx 整个组件就是一个纯函数接收transport一个 prop内部用Badge.withProps定义样式然后渲染标签——没有 store 访问、没有副作用。再比如 ServerCard.tsx它扩展了ServerEntry数据模型并声明了onToggleConnection、onConnectionInfo、onSettings、onEdit、onClone、onRemove等一整套回调 props。组件内部只负责把这些回调挂在按钮上具体连接、删除、克隆的逻辑全部由上层接线层实现。Storybook 只瞄准最底层——纯展示组件层。这正是整个组件体系可以被独立开发、独立验证的前提。三、Storybook 的角色隔离渲染、以故事为规格Storybook 提供一个独立的开发服务器在应用组件树之外单独渲染每个组件。开发者编写故事stories——本质上就是给组件指定 props 的具名配置。原文档给了一个UserCard的示例完整如下export const Default: Story { args: { user: { name: Ada Lovelace, role: Engineer }, onEdit: fn(), onDelete: fn(), }, }; export const LongName: Story { args: { user: { name: A Very Long Username That Might Break Layout, role: Admin }, onEdit: fn(), onDelete: fn(), }, };每个故事都会出现在侧边栏中你可以逐个点击在不同条件下对组件进行视觉验证。其中的fn()是 Storybook 提供的 mock 函数会把调用记录到 Actions 面板——你可以直观看到 onDelete was called with these arguments而没有任何真实逻辑执行。仓库中的真实故事文件与这个模式完全一致。以 AnnotationBadge.stories.tsx 为例它通过MetaStoryObj组织用args覆盖不同 facet 与取值const meta: Metatypeof AnnotationBadge { title: Elements/AnnotationBadge, component: AnnotationBadge, }; export const Audience: Story { args: { facet: audience, value: [user] }, }; export const ReadOnly: Story { args: { facet: readOnlyHint, value: true }, }; export const Destructive: Story { args: { facet: destructiveHint, value: true }, }; export const PriorityHigh: Story { args: { facet: priority, value: 0.9 }, };注意title: Elements/AnnotationBadge这种命名方式——它与 clients/web/src/components/elements 的目录层级一一对应让 Storybook 侧边栏天然成为组件分类树。四、Storybook 开发循环自底向上的必然典型的开发循环是定义组件的 props 接口即组件的模型基于这些 props 编写组件渲染逻辑编写覆盖各种 props 组合的故事空状态、错误状态、加载中、内容溢出等等在 Storybook UI 中逐一视觉验证。这一切都发生在组件接触真实数据、进入真实应用之前。这种流程会自然推动自底向上bottom-up的构建顺序先是 elements按钮、输入框、徽章再是 groups表单组、卡片然后是 screens完整面板、弹窗。你很难跳过中间层直接构建页面级组件因为它依赖的众多子组件还没有被隔离出来。这个分层结构在仓库中已经完整落地clients/web/src/components/elements —— 小而专一的单用途组件AnnotationBadge、TransportBadge、ConnectionToggle、LogEntry、ContentViewer、ProgressDisplay等clients/web/src/components/groups —— 由 elements 组合成的功能单元ServerCard、ToolDetailPanel、SchemaForm、SamplingRequestPanel、RootsTable等clients/web/src/components/screens —— 由 groups 组成的完整界面区域ToolsScreen、ResourcesScreen、LoggingScreen等clients/web/src/components/views —— 页面级视图如InspectorView。五、四条纪律把逻辑挡在组件之外文档特别强调React 让逻辑爬进组件变得非常容易因此需要内化几条原则。这是全文最具实践价值的部分。1. Props 作为完整契约如果组件需要知道用户是否有删除权限不要传入user.role让组件自己去判断而应传入canDelete: boolean。决策逻辑属于 store 或 hook不属于渲染层。这样做还有一个直接红利故事变得极其好写——只需把canDelete设为true或false即可。2. 用回调替代 store 访问如果展示组件直接 import 全局 store它就无法在不 mock 的情况下独立测试。正确做法是使用一个薄包装 hook 或容器通过useXxx()hooks 读取数据并把 props 传下去。展示组件只看到items: Item[]和onAdd: (item: Item) void。以仓库为例工具列表数据通过ManagedToolsState见 managedToolsState.ts与core/react中的 hooks 管理ToolsScreen等展示组件拿到的永远是已经接好线的 props。3. 派生状态放在外部过滤、排序、计算合计——这些都应该发生在 store selector 或自定义 hook 中而不是组件内部。组件接收的应该是已经过滤好的列表。这一点在 Apps 屏的设计中体现得尤其明显v2_ux.md 明确规定屏幕只接收已被上游过滤为 MCP Apps 的tools: Tool[]过滤逻辑放在接线层通过core/mcp/apps的共享isAppTool辅助函数完成展示组件不承担任何筛选职责。4. 本地 UI 状态是例外像下拉框是否展开当前激活哪个 tab这类状态天然属于组件内部用useState即可。判据是如果应用需要知道这个状态比如导航回来时要恢复它属于共享状态stores/hooks如果只有组件自己关心useState就足够。这是UI 状态与应用状态的清晰分界。六、Mantine 集成用 decorators 统一注入主题Mantine 提供了主题层和完整组件库因此 Storybook 需要用 Mantine 的MantineProvider包裹所有故事并注入项目的自定义主题。这是通过 Storybook 配置中的decorators完成的保证每个故事都能获得正确的样式上下文。文档指出两种做法手动编写全局 decorators或使用storybook-addon-mantine包简化这一过程。在当前仓库中主题定义位于 clients/web/src/themetheme.ts及按 Mantine 组件拆分的Badge.ts、Button.ts、Card.ts等样式覆盖文件Storybook 相关能力由 package.json 中引入的storybook/react-vite、storybook/addon-docs、storybook/addon-a11y、storybook/addon-vitest等插件提供。启动与构建 Storybook 的命令也在 package.json 中npm run storybook # storybook dev -p 6006开发预览 npm run build:storybook # storybook build产出静态站点 npm run test:storybook # 基于 vitest 的故事级测试七、Storybook 是唯一选择吗文档明确给出了横向对比结论务实Ladle更轻量的替代方案API 与 Storybook 类似HistoireVue 生态流行但也有 React 支持自建/dev路由组件画廊基础架构更少但会失去基于故事的编排方式和 addon 生态。对于当前技术栈选择 Storybook 的理由是它是最成熟的路径且对 TypeScript 的支持最好——能从 props 类型自动生成 controls。它可以从你的接口定义推断出可交互的控件让你在浏览器里直接调整 props 进行探索这在实际开发中非常有用。八、落地验证仓库中的证据链以上方法论并非纸上谈兵仓库里可以找到完整的验证链条分层目录即架构clients/web/src/components/{elements,groups,screens,views}四个层级与本文描述的自底向上构建顺序一一对应。props 契约模式TransportBadge.tsx 与 ServerCard.tsx 都严格遵循Props 接口 onVerb回调约定——这也是 v2_ux_storybook.md 中规定的统一命名规范回调用onVerb布尔标志用canVerb或isState。故事即规格每个组件目录下都配有同名.stories.tsx例如 AnnotationBadge.stories.tsx且每个组件至少包含Default故事与各状态变体故事。数据层独立core/mcp/state 中的ManagedToolsState、managedResourcesState等 store 负责所有数据获取与变更通知展示组件从不直接触达。九、总结纪律的本质是 props 接口设计文档的最终结论值得反复咀嚼真正的纪律不在于 Storybook而在于 props 接口设计。Storybook 只是让越界变得显而易见——当一个组件越过自己的 props 去外部取数据时为它编写故事就会变得异常痛苦这种摩擦本身就是最直接的反馈信号。围绕类型化模型props 接口设计组件、把决策逻辑留在 hooks/stores、用 Storybook 独立开发与验证视觉层——这套方法论构成了 Inspector V2 前端组件体系的基石。它同时服务于三个目标清晰的关注点分离、组件的独立可测试性以及可以持续演进、易于维护的组件库。更详细的组件清单、文件结构约定与构建顺序可继续阅读 v2_ux_storybook.md接口定义见 v2_ux_interfaces.md采样与引导式请求等客户端功能处理器的设计见 v2_ux_handlers.md。赞分享开发工具MCP Clients调试器【免费下载链接】inspectorVisual testing tool for MCP servers项目地址https://gitcode.com/gh_mirrors/inspector1/inspector点击查看免费下载相关推荐Owncast Web UI 组件开发指南函数组件模式、Error Boundary 与 Storybook 实践Owncast Web UI 组件开发指南函数组件模式、Error Boundary 与 Storybook 实践 Owncast 是开源自托管直播平台其音视频直播后端coze-studio 前端组件库模板 coze-studio/components基于 Storybook 的 React 组件开发与工程化实践coze studio 前端组件库模板 coze studio/components基于 Storybook 的 React 组件开发与工程化实践 coz人工智能AI Agent低代码RAG后端前端工作流自动化RedwoodJS 使用 Storybook 进行组件驱动开发启动、配置与深度实践指南RedwoodJS 使用 Storybook 进行组件驱动开发启动、配置与深度实践指南 导读 Storybook 是 RedwoodJS 官方推荐的组件驱动开后端前端Web框架开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考