diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000000..51cb758767 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "storybook": { + "type": "http", + "url": "http://localhost:6006/mcp" + } + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89ec9df3eb..eac019b221 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,6 +78,21 @@ npx knip --workspace packages/ui Architecture is documented in [ARCHITECTURE.md](./ARCHITECTURE.md); evaluation commands and contracts live in [`packages/eval`](./packages/eval). +### Storybook component documentation + +Storybook and its MCP endpoint are development-only and start on demand: + +```sh +npm --workspace @maka/desktop run storybook -- --no-open +``` + +The MCP endpoint is then available at `http://localhost:6006/mcp`; the root +`.mcp.json` registers it for clients that support repository MCP configuration. +Its component catalog is incremental rather than exhaustive. For UI work, +query Storybook documentation first. If the needed API is not documented there, +follow [DESIGN.md](./DESIGN.md) and verify Astryx components and props against +Astryx's official API or published type declarations. Never guess props. + ## Pull requests Opening a pull request pre-fills [`pull_request_template.md`](./.github/pull_request_template.md); fill it in rather than replacing it. diff --git a/CONTRIBUTING.zh-CN.md b/CONTRIBUTING.zh-CN.md index f7c4d3d041..2f73e8b441 100644 --- a/CONTRIBUTING.zh-CN.md +++ b/CONTRIBUTING.zh-CN.md @@ -78,6 +78,20 @@ npx knip --workspace packages/ui 架构说明见 [ARCHITECTURE.zh-CN.md](./ARCHITECTURE.zh-CN.md);Eval 的命令与 contract 见 [`packages/eval`](./packages/eval)。 +### Storybook 组件文档 + +Storybook 及其 MCP 端点仅用于开发,并按需启动: + +```sh +npm --workspace @maka/desktop run storybook -- --no-open +``` + +启动后,MCP 端点位于 `http://localhost:6006/mcp`;支持仓库级 MCP 配置的客户端 +会通过根目录的 `.mcp.json` 注册它。当前组件目录会逐步补充,并不完整。进行 UI +开发时应优先查询 Storybook 文档;如果所需 API 尚无文档,则遵循 +[DESIGN.md](./DESIGN.md),并根据 Astryx 官方 API 或已发布的类型声明核实 Astryx +组件及其 props,切勿猜测 props。 + ## Pull Request 开 PR 时会自动填充 [`pull_request_template.md`](./.github/pull_request_template.md);请在它的基础上填写,不要整段替换。 diff --git a/LICENSE b/LICENSE index 6371a078e8..6bd99b6925 100644 --- a/LICENSE +++ b/LICENSE @@ -308,6 +308,40 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Storybook MCP addon dependency patch + +Source: https://www.npmjs.com/package/@storybook/addon-mcp/v/0.7.0 +Repository: https://github.com/storybookjs/mcp/tree/main/packages/addon-mcp +Version: 0.7.0 +Dependency patch: patches/@storybook+addon-mcp+0.7.0.patch +Copyright (c) 2023 Storybook contributors +License: MIT + +Maka redistributes a source patch that replaces the addon's documentation +workflow instructions. The following MIT License applies to that material: + +MIT License + +Copyright (c) 2023 Storybook contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + node-pty dependency patch Source: https://www.npmjs.com/package/node-pty/v/1.2.0-beta.15 diff --git a/apps/desktop/.storybook/main.ts b/apps/desktop/.storybook/main.ts index d1040d7eac..93c95e71ce 100644 --- a/apps/desktop/.storybook/main.ts +++ b/apps/desktop/.storybook/main.ts @@ -35,6 +35,24 @@ const config: StorybookConfig = { '../../../packages/ui/stories/**/*.stories.@(ts|tsx)', '../stories/**/*.stories.@(ts|tsx)', ], + addons: [ + { + name: '@storybook/addon-mcp', + options: { + // Issue #3527: agents get a queryable component-docs interface. + // Docs is the default toolset selection, not an allowlist: the addon + // lets callers override it per request via the X-MCP-Toolsets + // header, so the security boundary is the dev server's loopback + // binding (see the storybook script in package.json). dev/test stay + // off by default; test additionally needs @storybook/addon-vitest. + toolsets: { + dev: false, + test: false, + docs: true, + }, + }, + }, + ], framework: { name: '@storybook/react-vite', options: {}, diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 2d396dab4b..9d7aedf125 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -18,7 +18,7 @@ "dev": "node scripts/dev.mjs", "dev:peer": "npm run prepare:runtime-host-peer && node scripts/dev.mjs --runtime-host-peer", "dev:hmr": "node scripts/dev.mjs", - "storybook": "storybook dev -p 6006 -c .storybook", + "storybook": "storybook dev -p 6006 -c .storybook --host 127.0.0.1 --exact-port", "build-storybook": "storybook build -c .storybook --output-dir storybook-static", "smoke:storybook": "node ../../scripts/storybook-visual-smoke.mjs", "check:architecture": "node --test scripts/check-renderer-architecture.test.mjs && node scripts/check-renderer-architecture.mjs", @@ -79,6 +79,7 @@ "@maka/ui": "0.1.0", "@modelcontextprotocol/sdk": "^1.26.0", "@playwright/test": "^1.62.1", + "@storybook/addon-mcp": "^0.7.0", "@storybook/react-vite": "^10.5.10", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", diff --git a/package-lock.json b/package-lock.json index 670c94ab33..e5f6bbe291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -71,6 +71,7 @@ "@maka/ui": "0.1.0", "@modelcontextprotocol/sdk": "^1.26.0", "@playwright/test": "^1.62.1", + "@storybook/addon-mcp": "^0.7.0", "@storybook/react-vite": "^10.5.10", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", @@ -3811,6 +3812,30 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, + "node_modules/@storybook/addon-mcp": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@storybook/addon-mcp/-/addon-mcp-0.7.0.tgz", + "integrity": "sha512-f/IWGRMzWynBg5kDJ3DYvvnafuSX88kykGNFzzLOlkLVpfxEZoAmQF6AS47tw25GuH6EFIqSo6ZDBLHLVyZ/IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/mcp": "0.8.0", + "@tmcp/adapter-valibot": "^0.1.5", + "@tmcp/transport-http": "^0.8.5", + "picoquery": "^2.5.0", + "tmcp": "^1.19.4", + "valibot": "1.2.0" + }, + "peerDependencies": { + "@storybook/addon-vitest": "^0.0.0-0 || ^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 || ^10.5.0-0", + "storybook": "^0.0.0-0 || ^9.1.16 || ^10.0.0 || ^10.1.0-0 || ^10.2.0-0 || ^10.3.0-0 || ^10.4.0-0 || ^10.5.0-0" + }, + "peerDependenciesMeta": { + "@storybook/addon-vitest": { + "optional": true + } + } + }, "node_modules/@storybook/builder-vite": { "version": "10.5.10", "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-10.5.10.tgz", @@ -3882,6 +3907,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@storybook/mcp": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@storybook/mcp/-/mcp-0.8.0.tgz", + "integrity": "sha512-G+XDgoWGrE98moXqKSee8fQyMQxoIWxRAbPG8r/HQ95pKodratevDqNyOgW+t6ZigM6AAVN1WHiFc5MI+hc8sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tmcp/adapter-valibot": "^0.1.5", + "@tmcp/transport-http": "^0.8.5", + "tmcp": "^1.19.4", + "valibot": "1.2.0" + } + }, "node_modules/@storybook/react": { "version": "10.5.10", "resolved": "https://registry.npmjs.org/@storybook/react/-/react-10.5.10.tgz", @@ -4064,6 +4102,77 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tmcp/adapter-valibot": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@tmcp/adapter-valibot/-/adapter-valibot-0.1.6.tgz", + "integrity": "sha512-drirZeNinhYLiRSMksN+m//u0ImFxtGRk1Vp425Xp/7CbBXFQdjAG+f7grssyHAukbVTGzmWsMMP6ejrGVErUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@valibot/to-json-schema": "^1.3.0", + "valibot": "^1.1.0" + }, + "peerDependencies": { + "tmcp": "^1.17.0", + "valibot": "^1.1.0" + } + }, + "node_modules/@tmcp/adapter-valibot/node_modules/@valibot/to-json-schema": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@valibot/to-json-schema/-/to-json-schema-1.7.1.tgz", + "integrity": "sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "valibot": "^1.4.0" + } + }, + "node_modules/@tmcp/adapter-valibot/node_modules/valibot": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz", + "integrity": "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@tmcp/session-manager": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@tmcp/session-manager/-/session-manager-0.2.2.tgz", + "integrity": "sha512-UrCRpTsxh5XnMbplspvftEYboiZWgAiXqqAUbyFTHoHMJ0LoNDy8bQd0+7qtxtT4S5Qsnv650gvs/Nbec5NTCQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tmcp": "^1.16.3" + } + }, + "node_modules/@tmcp/transport-http": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/@tmcp/transport-http/-/transport-http-0.8.6.tgz", + "integrity": "sha512-iLcxu+tEMbkVHbhFfyXQhxfPDDTfm+F0kEw8Xg/a1rm29s4cBg1vwcpbtk02XTxsdDh8RJ1AZkQwF9WDGeb/IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tmcp/session-manager": "^0.2.2", + "esm-env": "^1.2.2" + }, + "peerDependencies": { + "@tmcp/auth": "^0.3.3 || ^0.4.0", + "tmcp": "^1.18.0" + }, + "peerDependenciesMeta": { + "@tmcp/auth": { + "optional": true + } + } + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -7914,6 +8023,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -9300,6 +9416,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-rpc-2.0": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.8.0.tgz", + "integrity": "sha512-4nw+XlJbk5XokA7BtqHGu+0PEiUPfAkRzXXd1Cgjy1c3F2UI/3Gy7yr76wyJEv3X1F1SRGGF8xPAPPhelBiGmA==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -11344,6 +11467,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/picoquery": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/picoquery/-/picoquery-2.5.0.tgz", + "integrity": "sha512-j1kgOFxtaCyoFCkpoYG2Oj3OdGakadO7HZ7o5CqyRazlmBekKhbDoUnNnXASE07xSY4nDImWZkrZv7toSxMi/g==", + "dev": true, + "license": "MIT" + }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -12754,6 +12884,13 @@ "license": "BSD-3-Clause", "optional": true }, + "node_modules/sqids": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sqids/-/sqids-0.3.0.tgz", + "integrity": "sha512-lOQK1ucVg+W6n3FhRwwSeUijxe93b51Bfz5PMRMihVf1iVkl82ePQG7V5vwrhzB11v0NtsR25PSZRGiSomJaJw==", + "dev": true, + "license": "MIT" + }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -13423,6 +13560,20 @@ "node": ">=14.0.0" } }, + "node_modules/tmcp": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/tmcp/-/tmcp-1.20.0.tgz", + "integrity": "sha512-dcDximKQBGqLP/aEAVA26HcWRHhKipstg1w0fbDDJ0HcFZ6lolLU1YYmStYEn/73f534i7WrDNcjRfRYdV9CoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "json-rpc-2.0": "^1.7.1", + "sqids": "^0.3.0", + "uri-template-matcher": "^1.1.1", + "valibot": "^1.1.0" + } + }, "node_modules/tmp": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", @@ -13748,6 +13899,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-template-matcher": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/uri-template-matcher/-/uri-template-matcher-1.1.2.tgz", + "integrity": "sha512-uZc1h12jdO3m/R77SfTEOuo6VbMhgWznaawKpBjRGSJb7i91x5PgI37NQJtG+Cerxkk0yr1pylBY2qG1kQ+aEQ==", + "dev": true, + "license": "ISC" + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -13785,6 +13943,21 @@ "uuid": "dist-node/bin/uuid" } }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/patches/@storybook+addon-mcp+0.7.0.patch b/patches/@storybook+addon-mcp+0.7.0.patch new file mode 100644 index 0000000000..b67f17c2e2 --- /dev/null +++ b/patches/@storybook+addon-mcp+0.7.0.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@storybook/addon-mcp/dist/preset.js b/node_modules/@storybook/addon-mcp/dist/preset.js +index 956b95c..5f82bf8 100644 +--- a/node_modules/@storybook/addon-mcp/dist/preset.js ++++ b/node_modules/@storybook/addon-mcp/dist/preset.js +@@ -748 +748 @@ var legacy_test_instructions_default = "## Validation Workflow\n\n- After editin +-var review_docs_instructions_default = "## Documentation Workflow\n\n**CRITICAL: Never hallucinate component properties!** Undocumented props do not exist — never assume them from naming or other libraries; verify every prop via these tools, not source or types in node_modules.\n\n1. Call **list-all-documentation** once at task start for component and docs IDs.\n2. Call **get-documentation** with an `id` from that list for props and usage examples.\n\nOnly reference IDs returned by these tools — never guess; scope multi-source requests with `storybookId`.\n"; ++var maka_docs_instructions_default = "## Documentation Workflow\n\nUse Storybook documentation tools first to discover documented components, props, and examples. This catalog is incremental and incomplete: missing documentation does not mean a component or prop does not exist.\n\n1. Call **list-all-documentation** once at task start for available component and docs IDs.\n2. Call **get-documentation** with an `id` from that list for props and usage examples.\n3. Call **get-documentation-for-story** for additional details about a documented story variant.\n\nFor gaps, follow the repository's **DESIGN.md**. Verify Astryx components and props against Astryx's official API or published type declarations. Never guess props from names, conventions, or unrelated libraries. Only reference Storybook IDs returned by these tools; scope multi-source requests with `storybookId`.\n"; +@@ -773 +773 @@ function buildServerInstructions(options) { +- const sections = [options.docsEnabled ? "Follow these workflows when working with UI and/or Storybook. Answer questions about component props, API, or usage with the documentation tools — never from source or type definitions." : "Follow these workflows when working with UI and/or Storybook."]; ++ const sections = ["Follow these workflows when working with UI and/or Storybook."]; +@@ -783 +783 @@ function buildServerInstructions(options) { +- if (options.docsEnabled) sections.push((reviewEnabled ? review_docs_instructions_default : STORYBOOK_MCP_INSTRUCTIONS).trim()); ++ if (options.docsEnabled) sections.push(maka_docs_instructions_default.trim()); diff --git a/patches/README.md b/patches/README.md index 4169ece774..0fa8b67bf7 100644 --- a/patches/README.md +++ b/patches/README.md @@ -27,6 +27,18 @@ Keep this directory small. Prefer product code that uses the dependency's published API; only patch for bugs that block shipping and cannot be worked around at the call site. +## `@storybook/addon-mcp@0.7.0` + +The upstream MCP instructions treat Storybook documentation as an exhaustive +catalog and prohibit consulting source or type declarations. Maka's catalog is +incremental, so that advice would incorrectly hide existing components and +props. The patch keeps Storybook documentation first, states the coverage gap, +and directs agents to `DESIGN.md` plus Astryx's official API or published types +for missing documentation without guessing props. + +Delete when the addon supports project-owned instructions that can replace its +default documentation workflow. + ## `@tufjs/models@5.0.0` and `@sigstore/core@4.0.1` The published ECDSA verification paths rely on Node choosing a digest when