mcp-react-toolkit 1.3.1 → 1.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 +24 -24
- package/node_modules/@mcp-showcase/shared/build/McpServerBase.d.ts +13 -0
- package/node_modules/@mcp-showcase/shared/build/McpServerBase.d.ts.map +1 -1
- package/node_modules/@mcp-showcase/shared/build/McpServerBase.js +40 -0
- package/node_modules/@mcp-showcase/shared/build/McpServerBase.js.map +1 -1
- package/node_modules/@mcp-showcase/shared/build/types.d.ts +11 -0
- package/node_modules/@mcp-showcase/shared/build/types.d.ts.map +1 -1
- package/node_modules/@mcp-showcase/shared/src/McpServerBase.ts +45 -0
- package/node_modules/@mcp-showcase/shared/src/types.ts +12 -0
- package/node_modules/@mcp-showcase/ui-kit/README.md +30 -0
- package/node_modules/@mcp-showcase/ui-kit/build/components.d.ts +10 -0
- package/node_modules/@mcp-showcase/ui-kit/build/components.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/components.js +177 -0
- package/node_modules/@mcp-showcase/ui-kit/build/components.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/escape.d.ts +3 -0
- package/node_modules/@mcp-showcase/ui-kit/build/escape.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/escape.js +10 -0
- package/node_modules/@mcp-showcase/ui-kit/build/escape.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/fixture.d.ts +4 -0
- package/node_modules/@mcp-showcase/ui-kit/build/fixture.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/fixture.js +52 -0
- package/node_modules/@mcp-showcase/ui-kit/build/fixture.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/generate-template.d.ts +2 -0
- package/node_modules/@mcp-showcase/ui-kit/build/generate-template.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/generate-template.js +19 -0
- package/node_modules/@mcp-showcase/ui-kit/build/generate-template.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/index.d.ts +6 -0
- package/node_modules/@mcp-showcase/ui-kit/build/index.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/index.js +7 -0
- package/node_modules/@mcp-showcase/ui-kit/build/index.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/render.d.ts +3 -0
- package/node_modules/@mcp-showcase/ui-kit/build/render.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/render.js +38 -0
- package/node_modules/@mcp-showcase/ui-kit/build/render.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-components.d.ts +9 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-components.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-components.js +105 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-components.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.d.ts +4 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.js +39 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-render.d.ts +3 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-render.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-render.js +37 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-render.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.d.ts +2 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.js +72 -0
- package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/runtime.d.ts +2 -0
- package/node_modules/@mcp-showcase/ui-kit/build/runtime.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/runtime.js +221 -0
- package/node_modules/@mcp-showcase/ui-kit/build/runtime.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/theme.d.ts +2 -0
- package/node_modules/@mcp-showcase/ui-kit/build/theme.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/theme.js +278 -0
- package/node_modules/@mcp-showcase/ui-kit/build/theme.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/types.d.ts +113 -0
- package/node_modules/@mcp-showcase/ui-kit/build/types.d.ts.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/build/types.js +35 -0
- package/node_modules/@mcp-showcase/ui-kit/build/types.js.map +1 -0
- package/node_modules/@mcp-showcase/ui-kit/demo/index.html +653 -0
- package/node_modules/@mcp-showcase/ui-kit/demo/result.html +445 -0
- package/node_modules/@mcp-showcase/ui-kit/package.json +19 -0
- package/node_modules/@mcp-showcase/ui-kit/src/components.ts +191 -0
- package/node_modules/@mcp-showcase/ui-kit/src/escape.ts +9 -0
- package/node_modules/@mcp-showcase/ui-kit/src/fixture.ts +53 -0
- package/node_modules/@mcp-showcase/ui-kit/src/generate-template.ts +21 -0
- package/node_modules/@mcp-showcase/ui-kit/src/index.test.ts +72 -0
- package/node_modules/@mcp-showcase/ui-kit/src/index.ts +6 -0
- package/node_modules/@mcp-showcase/ui-kit/src/render.ts +48 -0
- package/node_modules/@mcp-showcase/ui-kit/src/result-components.ts +112 -0
- package/node_modules/@mcp-showcase/ui-kit/src/result-fixture.ts +40 -0
- package/node_modules/@mcp-showcase/ui-kit/src/result-render.test.ts +47 -0
- package/node_modules/@mcp-showcase/ui-kit/src/result-render.ts +47 -0
- package/node_modules/@mcp-showcase/ui-kit/src/result-runtime.ts +72 -0
- package/node_modules/@mcp-showcase/ui-kit/src/runtime.smoke.test.ts +103 -0
- package/node_modules/@mcp-showcase/ui-kit/src/runtime.ts +221 -0
- package/node_modules/@mcp-showcase/ui-kit/src/theme.ts +278 -0
- package/node_modules/@mcp-showcase/ui-kit/src/types.ts +140 -0
- package/node_modules/@mcp-showcase/ui-kit/tsconfig.json +9 -0
- package/package.json +6 -4
- package/tools/accessibility-checker/build/health-report.d.ts +27 -0
- package/tools/accessibility-checker/build/health-report.d.ts.map +1 -0
- package/tools/accessibility-checker/build/health-report.js +140 -0
- package/tools/accessibility-checker/build/health-report.js.map +1 -0
- package/tools/accessibility-checker/build/index.js +7 -1
- package/tools/accessibility-checker/build/index.js.map +1 -1
- package/tools/accessibility-checker/package.json +1 -0
- package/tools/code-modernizer/build/index.js +60 -44
- package/tools/code-modernizer/build/index.js.map +1 -1
- package/tools/code-modernizer/build/result-report.d.ts +24 -0
- package/tools/code-modernizer/build/result-report.d.ts.map +1 -0
- package/tools/code-modernizer/build/result-report.js +101 -0
- package/tools/code-modernizer/build/result-report.js.map +1 -0
- package/tools/code-modernizer/package.json +2 -0
- package/tools/component-factory/build/index.js +7 -1
- package/tools/component-factory/build/index.js.map +1 -1
- package/tools/component-factory/build/result-report.d.ts +11 -0
- package/tools/component-factory/build/result-report.d.ts.map +1 -0
- package/tools/component-factory/build/result-report.js +104 -0
- package/tools/component-factory/build/result-report.js.map +1 -0
- package/tools/component-factory/package.json +1 -0
- package/tools/component-fixer/build/index.js +6 -6
- package/tools/component-fixer/build/index.js.map +1 -1
- package/tools/component-fixer/build/result-report.d.ts +37 -0
- package/tools/component-fixer/build/result-report.d.ts.map +1 -0
- package/tools/component-fixer/build/result-report.js +106 -0
- package/tools/component-fixer/build/result-report.js.map +1 -0
- package/tools/component-fixer/package.json +1 -0
- package/tools/component-reviewer/build/health-report.d.ts +37 -0
- package/tools/component-reviewer/build/health-report.d.ts.map +1 -0
- package/tools/component-reviewer/build/health-report.js +116 -0
- package/tools/component-reviewer/build/health-report.js.map +1 -0
- package/tools/component-reviewer/build/index.d.ts.map +1 -1
- package/tools/component-reviewer/build/index.js +7 -6
- package/tools/component-reviewer/build/index.js.map +1 -1
- package/tools/component-reviewer/package.json +1 -0
- package/tools/dep-auditor/build/health-report.d.ts +16 -0
- package/tools/dep-auditor/build/health-report.d.ts.map +1 -0
- package/tools/dep-auditor/build/health-report.js +187 -0
- package/tools/dep-auditor/build/health-report.js.map +1 -0
- package/tools/dep-auditor/build/index.d.ts.map +1 -1
- package/tools/dep-auditor/build/index.js +7 -1
- package/tools/dep-auditor/build/index.js.map +1 -1
- package/tools/dep-auditor/package.json +1 -0
- package/tools/generate-tests/build/index.js +8 -1
- package/tools/generate-tests/build/index.js.map +1 -1
- package/tools/generate-tests/build/result-report.d.ts +14 -0
- package/tools/generate-tests/build/result-report.d.ts.map +1 -0
- package/tools/generate-tests/build/result-report.js +124 -0
- package/tools/generate-tests/build/result-report.js.map +1 -0
- package/tools/generate-tests/package.json +1 -0
- package/tools/legacy-analyzer/build/health-report.d.ts +4 -0
- package/tools/legacy-analyzer/build/health-report.d.ts.map +1 -0
- package/tools/legacy-analyzer/build/health-report.js +164 -0
- package/tools/legacy-analyzer/build/health-report.js.map +1 -0
- package/tools/legacy-analyzer/build/index.js +15 -1
- package/tools/legacy-analyzer/build/index.js.map +1 -1
- package/tools/legacy-analyzer/build/tools/01-detect-project-tech.d.ts.map +1 -1
- package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js +3 -8
- package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js.map +1 -1
- package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.d.ts.map +1 -1
- package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js +25 -3
- package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js.map +1 -1
- package/tools/legacy-analyzer/package.json +1 -0
- package/tools/lighthouse-runner/build/health-report.d.ts +14 -0
- package/tools/lighthouse-runner/build/health-report.d.ts.map +1 -0
- package/tools/lighthouse-runner/build/health-report.js +138 -0
- package/tools/lighthouse-runner/build/health-report.js.map +1 -0
- package/tools/lighthouse-runner/build/index.d.ts.map +1 -1
- package/tools/lighthouse-runner/build/index.js +7 -1
- package/tools/lighthouse-runner/build/index.js.map +1 -1
- package/tools/lighthouse-runner/package.json +1 -0
- package/tools/monorepo-manager/build/index.js +9 -1
- package/tools/monorepo-manager/build/index.js.map +1 -1
- package/tools/monorepo-manager/build/result-report.d.ts +20 -0
- package/tools/monorepo-manager/build/result-report.d.ts.map +1 -0
- package/tools/monorepo-manager/build/result-report.js +84 -0
- package/tools/monorepo-manager/build/result-report.js.map +1 -0
- package/tools/monorepo-manager/package.json +1 -0
- package/tools/performance-audit/build/health-report.d.ts +30 -0
- package/tools/performance-audit/build/health-report.d.ts.map +1 -0
- package/tools/performance-audit/build/health-report.js +152 -0
- package/tools/performance-audit/build/health-report.js.map +1 -0
- package/tools/performance-audit/build/index.d.ts.map +1 -1
- package/tools/performance-audit/build/index.js +7 -1
- package/tools/performance-audit/build/index.js.map +1 -1
- package/tools/performance-audit/package.json +1 -0
- package/tools/quality-pipeline/build/health-report.d.ts +11 -0
- package/tools/quality-pipeline/build/health-report.d.ts.map +1 -0
- package/tools/quality-pipeline/build/health-report.js +137 -0
- package/tools/quality-pipeline/build/health-report.js.map +1 -0
- package/tools/quality-pipeline/build/index.js +7 -1
- package/tools/quality-pipeline/build/index.js.map +1 -1
- package/tools/quality-pipeline/package.json +1 -0
- package/tools/render-analyzer/build/health-report.d.ts +33 -0
- package/tools/render-analyzer/build/health-report.d.ts.map +1 -0
- package/tools/render-analyzer/build/health-report.js +142 -0
- package/tools/render-analyzer/build/health-report.js.map +1 -0
- package/tools/render-analyzer/build/index.d.ts.map +1 -1
- package/tools/render-analyzer/build/index.js +7 -1
- package/tools/render-analyzer/build/index.js.map +1 -1
- package/tools/render-analyzer/package.json +1 -0
- package/tools/shared/build/McpServerBase.d.ts +13 -0
- package/tools/shared/build/McpServerBase.d.ts.map +1 -1
- package/tools/shared/build/McpServerBase.js +40 -0
- package/tools/shared/build/McpServerBase.js.map +1 -1
- package/tools/shared/build/types.d.ts +11 -0
- package/tools/shared/build/types.d.ts.map +1 -1
- package/tools/storybook-generator/build/index.d.ts.map +1 -1
- package/tools/storybook-generator/build/index.js +9 -1
- package/tools/storybook-generator/build/index.js.map +1 -1
- package/tools/storybook-generator/build/result-report.d.ts +22 -0
- package/tools/storybook-generator/build/result-report.d.ts.map +1 -0
- package/tools/storybook-generator/build/result-report.js +77 -0
- package/tools/storybook-generator/build/result-report.js.map +1 -0
- package/tools/storybook-generator/package.json +1 -0
- package/tools/test-gap-analyzer/build/health-report.d.ts +34 -0
- package/tools/test-gap-analyzer/build/health-report.d.ts.map +1 -0
- package/tools/test-gap-analyzer/build/health-report.js +190 -0
- package/tools/test-gap-analyzer/build/health-report.js.map +1 -0
- package/tools/test-gap-analyzer/build/index.d.ts.map +1 -1
- package/tools/test-gap-analyzer/build/index.js +7 -1
- package/tools/test-gap-analyzer/build/index.js.map +1 -1
- package/tools/test-gap-analyzer/package.json +1 -0
- package/tools/typescript-enforcer/build/health-report.d.ts +33 -0
- package/tools/typescript-enforcer/build/health-report.d.ts.map +1 -0
- package/tools/typescript-enforcer/build/health-report.js +143 -0
- package/tools/typescript-enforcer/build/health-report.js.map +1 -0
- package/tools/typescript-enforcer/build/index.js +6 -1
- package/tools/typescript-enforcer/build/index.js.map +1 -1
- package/tools/typescript-enforcer/package.json +1 -0
- package/tools/ui-kit/README.md +30 -0
- package/tools/ui-kit/build/components.d.ts +10 -0
- package/tools/ui-kit/build/components.d.ts.map +1 -0
- package/tools/ui-kit/build/components.js +177 -0
- package/tools/ui-kit/build/components.js.map +1 -0
- package/tools/ui-kit/build/escape.d.ts +3 -0
- package/tools/ui-kit/build/escape.d.ts.map +1 -0
- package/tools/ui-kit/build/escape.js +10 -0
- package/tools/ui-kit/build/escape.js.map +1 -0
- package/tools/ui-kit/build/fixture.d.ts +4 -0
- package/tools/ui-kit/build/fixture.d.ts.map +1 -0
- package/tools/ui-kit/build/fixture.js +52 -0
- package/tools/ui-kit/build/fixture.js.map +1 -0
- package/tools/ui-kit/build/generate-template.d.ts +2 -0
- package/tools/ui-kit/build/generate-template.d.ts.map +1 -0
- package/tools/ui-kit/build/generate-template.js +19 -0
- package/tools/ui-kit/build/generate-template.js.map +1 -0
- package/tools/ui-kit/build/index.d.ts +6 -0
- package/tools/ui-kit/build/index.d.ts.map +1 -0
- package/tools/ui-kit/build/index.js +7 -0
- package/tools/ui-kit/build/index.js.map +1 -0
- package/tools/ui-kit/build/render.d.ts +3 -0
- package/tools/ui-kit/build/render.d.ts.map +1 -0
- package/tools/ui-kit/build/render.js +38 -0
- package/tools/ui-kit/build/render.js.map +1 -0
- package/tools/ui-kit/build/result-components.d.ts +9 -0
- package/tools/ui-kit/build/result-components.d.ts.map +1 -0
- package/tools/ui-kit/build/result-components.js +105 -0
- package/tools/ui-kit/build/result-components.js.map +1 -0
- package/tools/ui-kit/build/result-fixture.d.ts +4 -0
- package/tools/ui-kit/build/result-fixture.d.ts.map +1 -0
- package/tools/ui-kit/build/result-fixture.js +39 -0
- package/tools/ui-kit/build/result-fixture.js.map +1 -0
- package/tools/ui-kit/build/result-render.d.ts +3 -0
- package/tools/ui-kit/build/result-render.d.ts.map +1 -0
- package/tools/ui-kit/build/result-render.js +37 -0
- package/tools/ui-kit/build/result-render.js.map +1 -0
- package/tools/ui-kit/build/result-runtime.d.ts +2 -0
- package/tools/ui-kit/build/result-runtime.d.ts.map +1 -0
- package/tools/ui-kit/build/result-runtime.js +72 -0
- package/tools/ui-kit/build/result-runtime.js.map +1 -0
- package/tools/ui-kit/build/runtime.d.ts +2 -0
- package/tools/ui-kit/build/runtime.d.ts.map +1 -0
- package/tools/ui-kit/build/runtime.js +221 -0
- package/tools/ui-kit/build/runtime.js.map +1 -0
- package/tools/ui-kit/build/theme.d.ts +2 -0
- package/tools/ui-kit/build/theme.d.ts.map +1 -0
- package/tools/ui-kit/build/theme.js +278 -0
- package/tools/ui-kit/build/theme.js.map +1 -0
- package/tools/ui-kit/build/types.d.ts +113 -0
- package/tools/ui-kit/build/types.d.ts.map +1 -0
- package/tools/ui-kit/build/types.js +35 -0
- package/tools/ui-kit/build/types.js.map +1 -0
- package/tools/ui-kit/package.json +19 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ MCP servers for React + TypeScript development automation. Works with Claude Des
|
|
|
6
6
|
[](https://github.com/Nishant-Chaudhary5338/mcp-toolkit/actions/workflows/ci.yml)
|
|
7
7
|
[](LICENSE)
|
|
8
8
|
[](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
9
|
-
[](#testing)
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -41,6 +41,27 @@ Swap in any tool name from `npx mcp-react-toolkit --list`. Restart your client a
|
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
+
## 🖥️ Interactive dashboards
|
|
45
|
+
|
|
46
|
+
Most MCP tools return raw JSON. The tools here return that JSON **plus a premium, interactive HTML dashboard** — a 0–100 health score, sortable issue triage, light/dark toggle, and one-click *fix* actions that call other tools in the toolkit.
|
|
47
|
+
|
|
48
|
+
It works three ways from a single self-contained artifact (no server, no external requests):
|
|
49
|
+
|
|
50
|
+
| Where you run it | What you get |
|
|
51
|
+
|---|---|
|
|
52
|
+
| **Claude Desktop** (MCP Apps) | The dashboard renders **inline in the conversation** (sandboxed iframe); action buttons drive the agent. |
|
|
53
|
+
| **Claude Code (VS Code) · Cursor · CLI** | The JSON **plus a clickable `file://` link** — open it to view the full dashboard in your browser. |
|
|
54
|
+
| **Any browser** | The same HTML opens standalone — fully interactive. |
|
|
55
|
+
|
|
56
|
+
Two dashboard styles:
|
|
57
|
+
|
|
58
|
+
- **Audit view** — `legacy-analyzer`, `component-reviewer`, `accessibility-checker`, `dep-auditor`, `typescript-enforcer`, `performance-audit`, `render-analyzer`, `test-gap-analyzer`, `quality-pipeline`, `lighthouse-runner`. Health score, grade, category cards, filter/sort issue table.
|
|
59
|
+
- **Result view** — `component-factory`, `component-fixer`, `code-modernizer`, `storybook-generator`, `generate-tests`, `monorepo-manager`. Files created/changed, diffs, and follow-up actions.
|
|
60
|
+
|
|
61
|
+
**How it renders:** the tool returns an MCP `resource` with a `ui://` URI and `mimeType: text/html`. Hosts that support [MCP Apps](https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/) render it inline; for every other client the toolkit also writes the HTML to a temp file and returns a `file://` link so you can open it in a browser. Powered by the internal `@mcp-showcase/ui-kit` package — dependency-free, dual light/dark, ~30 KB per report.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
44
65
|
## What's here
|
|
45
66
|
|
|
46
67
|
```
|
|
@@ -184,7 +205,7 @@ git clone https://github.com/Nishant-Chaudhary5338/mcp-toolkit.git
|
|
|
184
205
|
cd mcp-toolkit
|
|
185
206
|
npm install
|
|
186
207
|
npm run build
|
|
187
|
-
npm test #
|
|
208
|
+
npm test # run the full suite across all tools
|
|
188
209
|
npm run dev # server on :3002, client on :5173
|
|
189
210
|
```
|
|
190
211
|
|
|
@@ -326,34 +347,13 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node tools/legacy-analyz
|
|
|
326
347
|
|
|
327
348
|
## Testing
|
|
328
349
|
|
|
329
|
-
|
|
350
|
+
Every tool has a co-located Vitest suite covering its core logic directly — no MCP transport required — plus tests for the dashboard renderers and per-tool report mappers.
|
|
330
351
|
|
|
331
352
|
```sh
|
|
332
353
|
npm test # all tools
|
|
333
354
|
npm run test -w tools/legacy-analyzer # single tool
|
|
334
355
|
```
|
|
335
356
|
|
|
336
|
-
| Tool | Tests |
|
|
337
|
-
|---|---|
|
|
338
|
-
| render-analyzer | 11 |
|
|
339
|
-
| storybook-generator | 20 |
|
|
340
|
-
| performance-audit | 15 |
|
|
341
|
-
| lighthouse-runner | 13 |
|
|
342
|
-
| test-gap-analyzer | 15 |
|
|
343
|
-
| component-reviewer | 19 |
|
|
344
|
-
| component-fixer | 10 |
|
|
345
|
-
| legacy-analyzer | 14 |
|
|
346
|
-
| json-viewer | 16 |
|
|
347
|
-
| quality-pipeline | 8 |
|
|
348
|
-
| component-factory | 6 |
|
|
349
|
-
| code-modernizer | 8 |
|
|
350
|
-
| dep-auditor | 15 |
|
|
351
|
-
| accessibility-checker | 15 |
|
|
352
|
-
| generate-tests | 14 |
|
|
353
|
-
| typescript-enforcer | 22 |
|
|
354
|
-
| monorepo-manager | 30 |
|
|
355
|
-
| **Total** | **450** |
|
|
356
|
-
|
|
357
357
|
CI runs on every push and PR against Node 20 and 22.
|
|
358
358
|
|
|
359
359
|
---
|
|
@@ -11,6 +11,19 @@ export declare abstract class McpServerBase {
|
|
|
11
11
|
private setupHandlers;
|
|
12
12
|
private setupErrorHandlers;
|
|
13
13
|
protected success<T extends Record<string, unknown>>(data: T): ToolResult;
|
|
14
|
+
/**
|
|
15
|
+
* Return machine-readable data (so the model can reason over it), an
|
|
16
|
+
* interactive UI resource (MCP Apps hosts render it inline in a sandboxed
|
|
17
|
+
* iframe), AND a clickable file:// link to the same dashboard written to a
|
|
18
|
+
* temp file — so clients without MCP-Apps rendering (e.g. Claude Code in
|
|
19
|
+
* VS Code) can still open the visual report in a browser.
|
|
20
|
+
*/
|
|
21
|
+
protected successWithUI<T extends Record<string, unknown>>(data: T, ui: {
|
|
22
|
+
uri: string;
|
|
23
|
+
html: string;
|
|
24
|
+
}): ToolResult;
|
|
25
|
+
/** Write the dashboard HTML to a stable temp file; return its file:// URL (null on failure). */
|
|
26
|
+
private writeReportFile;
|
|
14
27
|
protected error(error: unknown): ToolResult;
|
|
15
28
|
run(): Promise<void>;
|
|
16
29
|
shutdown(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerBase.d.ts","sourceRoot":"","sources":["../src/McpServerBase.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"McpServerBase.d.ts","sourceRoot":"","sources":["../src/McpServerBase.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAWnE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExF,8BAAsB,aAAa;IACjC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC;IACjC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;gBAEnB,MAAM,EAAE,YAAY;IAchC,SAAS,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI;IAExC,SAAS,CAAC,OAAO,CACf,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,OAAO,EAAE,WAAW,GACnB,IAAI;IAIP,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,kBAAkB;IAS1B,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU;IAMzE;;;;;;OAMG;IACH,SAAS,CAAC,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvD,IAAI,EAAE,CAAC,EACP,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAChC,UAAU;IAkBb,gGAAgG;IAChG,OAAO,CAAC,eAAe;IAavB,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU;IAQrC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAIhC"}
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
6
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
|
|
7
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
7
10
|
import { ToolRegistry } from './ToolRegistry.js';
|
|
8
11
|
export class McpServerBase {
|
|
9
12
|
server;
|
|
@@ -54,6 +57,43 @@ export class McpServerBase {
|
|
|
54
57
|
content: [{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) }],
|
|
55
58
|
};
|
|
56
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Return machine-readable data (so the model can reason over it), an
|
|
62
|
+
* interactive UI resource (MCP Apps hosts render it inline in a sandboxed
|
|
63
|
+
* iframe), AND a clickable file:// link to the same dashboard written to a
|
|
64
|
+
* temp file — so clients without MCP-Apps rendering (e.g. Claude Code in
|
|
65
|
+
* VS Code) can still open the visual report in a browser.
|
|
66
|
+
*/
|
|
67
|
+
successWithUI(data, ui) {
|
|
68
|
+
const content = [
|
|
69
|
+
{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) },
|
|
70
|
+
];
|
|
71
|
+
const fileUrl = this.writeReportFile(ui.uri, ui.html);
|
|
72
|
+
if (fileUrl) {
|
|
73
|
+
content.push({
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: `📊 Interactive dashboard: ${fileUrl}\n` +
|
|
76
|
+
`Open that link in a browser to explore the report (sortable issues, theme toggle, fix actions). ` +
|
|
77
|
+
`In Claude Desktop it renders inline automatically.`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
content.push({ type: 'resource', resource: { uri: ui.uri, mimeType: 'text/html', text: ui.html } });
|
|
81
|
+
return { content };
|
|
82
|
+
}
|
|
83
|
+
/** Write the dashboard HTML to a stable temp file; return its file:// URL (null on failure). */
|
|
84
|
+
writeReportFile(uri, html) {
|
|
85
|
+
try {
|
|
86
|
+
const slug = uri.replace(/^ui:\/\//, '').replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'report';
|
|
87
|
+
const dir = join(tmpdir(), 'mcp-react-toolkit');
|
|
88
|
+
mkdirSync(dir, { recursive: true });
|
|
89
|
+
const filePath = join(dir, `${slug}.html`);
|
|
90
|
+
writeFileSync(filePath, html, 'utf8');
|
|
91
|
+
return `file://${filePath}`;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
57
97
|
error(error) {
|
|
58
98
|
const msg = error instanceof Error ? error.message : String(error);
|
|
59
99
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"McpServerBase.js","sourceRoot":"","sources":["../src/McpServerBase.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"McpServerBase.js","sourceRoot":"","sources":["../src/McpServerBase.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,4DAA4D;AAC5D,+EAA+E;AAE/E,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,SAAS,EACT,sBAAsB,EACtB,QAAQ,GACT,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,MAAM,OAAgB,aAAa;IACvB,MAAM,CAAS;IACf,QAAQ,CAAe;IACvB,MAAM,CAAe;IAE/B,YAAY,MAAoB;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;QAEnC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CACtB,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAC9C,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;QAEF,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAIS,OAAO,CACf,IAAY,EACZ,WAAmB,EACnB,WAA0C,EAC1C,OAAoB;QAEpB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACjE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE;SACzC,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACrE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,iBAAiB,IAAI,EAAE,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,QAAQ;oBAAE,MAAM,KAAK,CAAC;gBAC3C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,0BAA0B,OAAO,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,kBAAkB;QACxB,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YAC9B,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,cAAc,EAAE,KAAK,CAAC,CAAC;QAC3D,CAAC,CAAC;QACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IAES,OAAO,CAAoC,IAAO;QAC1D,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SACvF,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACO,aAAa,CACrB,IAAO,EACP,EAAiC;QAEjC,MAAM,OAAO,GAA0B;YACrC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;SAC5E,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI,EACF,6BAA6B,OAAO,IAAI;oBACxC,kGAAkG;oBAClG,oDAAoD;aACvD,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpG,OAAO,EAAE,OAAO,EAAE,CAAC;IACrB,CAAC;IAED,gGAAgG;IACxF,eAAe,CAAC,GAAW,EAAE,IAAY;QAC/C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC;YAC5G,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC;YAChD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;YAC3C,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YACtC,OAAO,UAAU,QAAQ,EAAE,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAES,KAAK,CAAC,KAAc;QAC5B,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YAC1F,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,gBAAgB,IAAI,CAAC,MAAM,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAC3F,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -21,11 +21,22 @@ export interface ToolResult {
|
|
|
21
21
|
_meta?: Record<string, unknown>;
|
|
22
22
|
[key: string]: unknown;
|
|
23
23
|
}
|
|
24
|
+
export interface EmbeddedResource {
|
|
25
|
+
/** Resource identifier. UI resources use the `ui://` scheme (MCP Apps / MCP-UI). */
|
|
26
|
+
uri: string;
|
|
27
|
+
mimeType?: string;
|
|
28
|
+
/** Inline text payload (e.g. self-contained HTML for a `text/html` UI resource). */
|
|
29
|
+
text?: string;
|
|
30
|
+
/** Base64 binary payload. */
|
|
31
|
+
blob?: string;
|
|
32
|
+
}
|
|
24
33
|
export interface ToolContent {
|
|
25
34
|
type: 'text' | 'image' | 'resource';
|
|
26
35
|
text?: string;
|
|
27
36
|
data?: string;
|
|
28
37
|
mimeType?: string;
|
|
38
|
+
/** Present when `type === 'resource'`. */
|
|
39
|
+
resource?: EmbeddedResource;
|
|
29
40
|
}
|
|
30
41
|
export interface ServerConfig {
|
|
31
42
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC"}
|
|
@@ -10,6 +10,9 @@ import {
|
|
|
10
10
|
ListToolsRequestSchema,
|
|
11
11
|
McpError,
|
|
12
12
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
13
16
|
import { ToolRegistry } from './ToolRegistry.js';
|
|
14
17
|
import type { ServerConfig, ToolDefinition, ToolHandler, ToolResult } from './types.js';
|
|
15
18
|
|
|
@@ -79,6 +82,48 @@ export abstract class McpServerBase {
|
|
|
79
82
|
};
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Return machine-readable data (so the model can reason over it), an
|
|
87
|
+
* interactive UI resource (MCP Apps hosts render it inline in a sandboxed
|
|
88
|
+
* iframe), AND a clickable file:// link to the same dashboard written to a
|
|
89
|
+
* temp file — so clients without MCP-Apps rendering (e.g. Claude Code in
|
|
90
|
+
* VS Code) can still open the visual report in a browser.
|
|
91
|
+
*/
|
|
92
|
+
protected successWithUI<T extends Record<string, unknown>>(
|
|
93
|
+
data: T,
|
|
94
|
+
ui: { uri: string; html: string }
|
|
95
|
+
): ToolResult {
|
|
96
|
+
const content: ToolResult['content'] = [
|
|
97
|
+
{ type: 'text', text: JSON.stringify({ success: true, ...data }, null, 2) },
|
|
98
|
+
];
|
|
99
|
+
const fileUrl = this.writeReportFile(ui.uri, ui.html);
|
|
100
|
+
if (fileUrl) {
|
|
101
|
+
content.push({
|
|
102
|
+
type: 'text',
|
|
103
|
+
text:
|
|
104
|
+
`📊 Interactive dashboard: ${fileUrl}\n` +
|
|
105
|
+
`Open that link in a browser to explore the report (sortable issues, theme toggle, fix actions). ` +
|
|
106
|
+
`In Claude Desktop it renders inline automatically.`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
content.push({ type: 'resource', resource: { uri: ui.uri, mimeType: 'text/html', text: ui.html } });
|
|
110
|
+
return { content };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Write the dashboard HTML to a stable temp file; return its file:// URL (null on failure). */
|
|
114
|
+
private writeReportFile(uri: string, html: string): string | null {
|
|
115
|
+
try {
|
|
116
|
+
const slug = uri.replace(/^ui:\/\//, '').replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'report';
|
|
117
|
+
const dir = join(tmpdir(), 'mcp-react-toolkit');
|
|
118
|
+
mkdirSync(dir, { recursive: true });
|
|
119
|
+
const filePath = join(dir, `${slug}.html`);
|
|
120
|
+
writeFileSync(filePath, html, 'utf8');
|
|
121
|
+
return `file://${filePath}`;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
82
127
|
protected error(error: unknown): ToolResult {
|
|
83
128
|
const msg = error instanceof Error ? error.message : String(error);
|
|
84
129
|
return {
|
|
@@ -28,11 +28,23 @@ export interface ToolResult {
|
|
|
28
28
|
[key: string]: unknown;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export interface EmbeddedResource {
|
|
32
|
+
/** Resource identifier. UI resources use the `ui://` scheme (MCP Apps / MCP-UI). */
|
|
33
|
+
uri: string;
|
|
34
|
+
mimeType?: string;
|
|
35
|
+
/** Inline text payload (e.g. self-contained HTML for a `text/html` UI resource). */
|
|
36
|
+
text?: string;
|
|
37
|
+
/** Base64 binary payload. */
|
|
38
|
+
blob?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
export interface ToolContent {
|
|
32
42
|
type: 'text' | 'image' | 'resource';
|
|
33
43
|
text?: string;
|
|
34
44
|
data?: string;
|
|
35
45
|
mimeType?: string;
|
|
46
|
+
/** Present when `type === 'resource'`. */
|
|
47
|
+
resource?: EmbeddedResource;
|
|
36
48
|
}
|
|
37
49
|
|
|
38
50
|
export interface ServerConfig {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @mcp-showcase/ui-kit
|
|
2
|
+
|
|
3
|
+
Reusable **single-file HTML report UI** for MCP tools — dependency-free, dual-theme (light/dark), and agentic. Any tool that maps its output to the `HealthReport` schema gets a premium interactive dashboard that renders inside an MCP host (MCP Apps / `ui://`) **and** opens standalone in a browser.
|
|
4
|
+
|
|
5
|
+
Flagship producer: `legacy-analyzer` → **Codebase Health Studio**.
|
|
6
|
+
|
|
7
|
+
## Exports
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { renderReportHTML, SAMPLE_REPORT, type HealthReport } from "@mcp-showcase/ui-kit";
|
|
11
|
+
|
|
12
|
+
const html = renderReportHTML(myReport); // one self-contained <!doctype html> string
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `renderReportHTML(report: HealthReport): string` — styles, runtime and data all inlined (no external requests, safe for a sandboxed iframe).
|
|
16
|
+
- `HealthReport` / `ReportIssue` / `ReportCategory` / `ReportAction` — the tool-agnostic contract.
|
|
17
|
+
- `SAMPLE_REPORT` — fixture used by the standalone demo (`npm run build` → `demo/index.html`).
|
|
18
|
+
|
|
19
|
+
## Surfaces (DataAdapter)
|
|
20
|
+
|
|
21
|
+
The runtime detects where it is running:
|
|
22
|
+
|
|
23
|
+
- **Embedded in an MCP host** → action buttons `postMessage` MCP-UI intents (`tool` / `prompt`) to the parent so the agent runs the fix.
|
|
24
|
+
- **Standalone browser tab** → graceful fallback: copies a ready-to-paste prompt to the clipboard.
|
|
25
|
+
|
|
26
|
+
Data is read from `window.__REPORT__`, then an inline `#report-data` script, then a `?data=` base64 URL param.
|
|
27
|
+
|
|
28
|
+
## Why no framework
|
|
29
|
+
|
|
30
|
+
The artifact must be a tiny, robust, self-contained file for a sandboxed iframe. Plain CSS + browser JS keeps the bundle ~38 KB with zero supply chain. The React/Vite path is a documented future swap if richer interactivity is needed.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HealthReport, ReportAction } from "./types.js";
|
|
2
|
+
export declare function buildHeader(report: HealthReport): string;
|
|
3
|
+
export declare function buildHero(report: HealthReport): string;
|
|
4
|
+
declare function actionButtons(actions: ReportAction[] | undefined, primaryFirst?: boolean): string;
|
|
5
|
+
export declare function buildFixFirst(report: HealthReport): string;
|
|
6
|
+
export declare function buildCategories(report: HealthReport): string;
|
|
7
|
+
export declare function buildTriageShell(report: HealthReport): string;
|
|
8
|
+
export declare function buildChrome(): string;
|
|
9
|
+
export { actionButtons };
|
|
10
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,YAAY,EAEZ,YAAY,EAGb,MAAM,YAAY,CAAC;AAcpB,wBAAgB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAsBxD;AAsBD,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAkBtD;AAED,iBAAS,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,EAAE,YAAY,UAAQ,GAAG,MAAM,CAQxF;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAiB1D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAkB5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA+B7D;AAED,wBAAgB,WAAW,IAAI,MAAM,CAcpC;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// SERVER-SIDE COMPONENT BUILDERS — pure string -> HTML. No framework.
|
|
3
|
+
// The hero, category cards and fix-first queue are rendered statically for an
|
|
4
|
+
// instant premium paint; the interactive triage table + drawer are hydrated
|
|
5
|
+
// client-side by runtime.ts from the injected report data.
|
|
6
|
+
// ============================================================================
|
|
7
|
+
import { esc } from "./escape.js";
|
|
8
|
+
import { scoreToBand, scoreToGrade, } from "./types.js";
|
|
9
|
+
const SUN = `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>`;
|
|
10
|
+
const MOON = `<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>`;
|
|
11
|
+
const SEARCH = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>`;
|
|
12
|
+
function verdict(score) {
|
|
13
|
+
if (score >= 85)
|
|
14
|
+
return "Healthy & well-structured";
|
|
15
|
+
if (score >= 70)
|
|
16
|
+
return "Solid, with a few rough edges";
|
|
17
|
+
if (score >= 55)
|
|
18
|
+
return "Workable, but carrying debt";
|
|
19
|
+
if (score >= 40)
|
|
20
|
+
return "Significant tech debt";
|
|
21
|
+
return "High-risk — needs intervention";
|
|
22
|
+
}
|
|
23
|
+
export function buildHeader(report) {
|
|
24
|
+
return /* html */ `
|
|
25
|
+
<header class="hdr">
|
|
26
|
+
<div class="brand">
|
|
27
|
+
<span class="dot" aria-hidden="true">
|
|
28
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
|
|
29
|
+
</span>
|
|
30
|
+
<div style="min-width:0">
|
|
31
|
+
<h1>${esc(report.meta.title)}</h1>
|
|
32
|
+
<p class="sub">${esc(report.meta.target)}</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="hdr-actions">
|
|
36
|
+
<button class="btn icon" id="theme-toggle" type="button" aria-label="Toggle colour theme" title="Toggle theme">
|
|
37
|
+
<span class="theme-sun" hidden>${SUN}</span><span class="theme-moon">${MOON}</span>
|
|
38
|
+
</button>
|
|
39
|
+
<button class="btn" id="export-btn" type="button" title="Copy report as Markdown">
|
|
40
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
|
41
|
+
Export
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
</header>`;
|
|
45
|
+
}
|
|
46
|
+
function gauge(score) {
|
|
47
|
+
const r = 64;
|
|
48
|
+
const circ = 2 * Math.PI * r;
|
|
49
|
+
const pct = Math.max(0, Math.min(100, score)) / 100;
|
|
50
|
+
const offset = circ * (1 - pct);
|
|
51
|
+
return /* html */ `
|
|
52
|
+
<div class="gauge" role="img" aria-label="Health score ${esc(score)} out of 100">
|
|
53
|
+
<svg width="148" height="148" viewBox="0 0 148 148">
|
|
54
|
+
<circle class="track" cx="74" cy="74" r="${r}" stroke-width="11"/>
|
|
55
|
+
<circle class="arc" cx="74" cy="74" r="${r}" stroke-width="11"
|
|
56
|
+
stroke-dasharray="${circ.toFixed(1)}" stroke-dashoffset="${circ.toFixed(1)}"
|
|
57
|
+
data-target="${offset.toFixed(1)}"/>
|
|
58
|
+
</svg>
|
|
59
|
+
<div class="center">
|
|
60
|
+
<div class="score num" data-count="${esc(score)}">0</div>
|
|
61
|
+
<div class="of">/ 100</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>`;
|
|
64
|
+
}
|
|
65
|
+
export function buildHero(report) {
|
|
66
|
+
const band = scoreToBand(report.score);
|
|
67
|
+
const grade = scoreToGrade(report.score);
|
|
68
|
+
const chips = (report.chips ?? [])
|
|
69
|
+
.map((c) => `<span class="chip"><span>${esc(c.label)}</span><b>${esc(c.value)}</b></span>`)
|
|
70
|
+
.join("");
|
|
71
|
+
return /* html */ `
|
|
72
|
+
<section class="hero" data-band="${band}">
|
|
73
|
+
${gauge(report.score)}
|
|
74
|
+
<div class="hero-meta">
|
|
75
|
+
<div class="grade-row">
|
|
76
|
+
<span class="grade">Grade ${esc(grade)}</span>
|
|
77
|
+
<span class="verdict">${esc(verdict(report.score))}</span>
|
|
78
|
+
</div>
|
|
79
|
+
<p>${esc(report.totalIssues)} issue${report.totalIssues === 1 ? "" : "s"} found across ${esc(report.categories.length)} areas${report.meta.subtitle ? " · " + esc(report.meta.subtitle) : ""}.</p>
|
|
80
|
+
<div class="chips">${chips}</div>
|
|
81
|
+
</div>
|
|
82
|
+
</section>`;
|
|
83
|
+
}
|
|
84
|
+
function actionButtons(actions, primaryFirst = false) {
|
|
85
|
+
if (!actions || !actions.length)
|
|
86
|
+
return "";
|
|
87
|
+
return actions
|
|
88
|
+
.map((a, i) => {
|
|
89
|
+
const cls = primaryFirst && i === 0 ? "btn primary" : "btn";
|
|
90
|
+
return `<button class="${cls}" type="button" data-action="${esc(a.id)}">${esc(a.label)}</button>`;
|
|
91
|
+
})
|
|
92
|
+
.join("");
|
|
93
|
+
}
|
|
94
|
+
export function buildFixFirst(report) {
|
|
95
|
+
const actions = report.topActions ?? [];
|
|
96
|
+
if (!actions.length)
|
|
97
|
+
return "";
|
|
98
|
+
const items = actions
|
|
99
|
+
.map((a, i) => {
|
|
100
|
+
const sub = a.kind === "tool" ? `Runs ${esc(a.tool)}` : a.kind === "prompt" ? "Asks the agent" : "Opens link";
|
|
101
|
+
return /* html */ `
|
|
102
|
+
<div class="qitem" data-action="${esc(a.id)}" role="button" tabindex="0">
|
|
103
|
+
<span class="rank num">${i + 1}</span>
|
|
104
|
+
<span class="qt"><span class="t">${esc(a.label)}</span><span class="s">${sub}</span></span>
|
|
105
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--faint)"><path d="m9 18 6-6-6-6"/></svg>
|
|
106
|
+
</div>`;
|
|
107
|
+
})
|
|
108
|
+
.join("");
|
|
109
|
+
return /* html */ `
|
|
110
|
+
<div class="sec"><h2>Fix-first queue</h2><span class="count">${actions.length} prioritised</span></div>
|
|
111
|
+
<div class="queue">${items}</div>`;
|
|
112
|
+
}
|
|
113
|
+
export function buildCategories(report) {
|
|
114
|
+
const card = (c) => {
|
|
115
|
+
const hasScore = typeof c.score === "number";
|
|
116
|
+
const pct = hasScore ? Math.max(0, Math.min(100, c.score)) : 0;
|
|
117
|
+
return /* html */ `
|
|
118
|
+
<div class="card" data-category="${esc(c.id)}" role="button" tabindex="0" aria-label="Filter issues by ${esc(c.name)}">
|
|
119
|
+
<div class="top">
|
|
120
|
+
<span class="name">${esc(c.name)}</span>
|
|
121
|
+
<span class="badge ${esc(c.status)}">${hasScore ? esc(pct) : esc(c.status)}</span>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="sum">${esc(c.summary)}</div>
|
|
124
|
+
${hasScore ? `<div class="bar"><i class="${esc(c.status)}" data-w="${pct}" style="width:0"></i></div>` : ""}
|
|
125
|
+
<div class="foot"><span>${esc(c.issueCount)} issue${c.issueCount === 1 ? "" : "s"}</span><span>View →</span></div>
|
|
126
|
+
</div>`;
|
|
127
|
+
};
|
|
128
|
+
return /* html */ `
|
|
129
|
+
<div class="sec"><h2>Areas</h2><span class="count">${report.categories.length} analysed</span></div>
|
|
130
|
+
<div class="cards">${report.categories.map(card).join("")}</div>`;
|
|
131
|
+
}
|
|
132
|
+
export function buildTriageShell(report) {
|
|
133
|
+
const sevFilters = ["all", "critical", "high", "medium", "low"]
|
|
134
|
+
.map((s, i) => `<button type="button" data-sev="${s}" aria-pressed="${i === 0 ? "true" : "false"}">${s[0].toUpperCase() + s.slice(1)}</button>`)
|
|
135
|
+
.join("");
|
|
136
|
+
return /* html */ `
|
|
137
|
+
<div class="sec"><h2>Issues</h2><span class="count" id="issue-count">${report.issues.length} total</span></div>
|
|
138
|
+
<div class="toolbar">
|
|
139
|
+
<label class="search">
|
|
140
|
+
${SEARCH}
|
|
141
|
+
<input id="search" type="search" placeholder="Search issues, files, categories…" aria-label="Search issues"/>
|
|
142
|
+
</label>
|
|
143
|
+
<div class="filter" role="group" aria-label="Filter by severity">${sevFilters}</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="table-card">
|
|
146
|
+
<table>
|
|
147
|
+
<thead><tr>
|
|
148
|
+
<th data-sort="severity" aria-sort="descending">Severity<span class="arr">▼</span></th>
|
|
149
|
+
<th data-sort="title">Issue<span class="arr">▼</span></th>
|
|
150
|
+
<th data-sort="category">Area<span class="arr">▼</span></th>
|
|
151
|
+
<th data-sort="file">File<span class="arr">▼</span></th>
|
|
152
|
+
</tr></thead>
|
|
153
|
+
<tbody id="rows"></tbody>
|
|
154
|
+
</table>
|
|
155
|
+
<div class="empty" id="empty" hidden>
|
|
156
|
+
<div class="big">Nothing matches</div>
|
|
157
|
+
<div>Try clearing the search or severity filter.</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>`;
|
|
160
|
+
}
|
|
161
|
+
export function buildChrome() {
|
|
162
|
+
return /* html */ `
|
|
163
|
+
<div class="scrim" id="scrim"></div>
|
|
164
|
+
<aside class="drawer" id="drawer" role="dialog" aria-modal="true" aria-labelledby="d-title">
|
|
165
|
+
<header>
|
|
166
|
+
<h3 id="d-title"></h3>
|
|
167
|
+
<button class="btn icon" id="d-close" type="button" aria-label="Close detail">
|
|
168
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
|
|
169
|
+
</button>
|
|
170
|
+
</header>
|
|
171
|
+
<div class="body" id="d-body"></div>
|
|
172
|
+
<div class="acts" id="d-acts"></div>
|
|
173
|
+
</aside>
|
|
174
|
+
<div class="toast" id="toast" role="status" aria-live="polite"></div>`;
|
|
175
|
+
}
|
|
176
|
+
export { actionButtons };
|
|
177
|
+
//# sourceMappingURL=components.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAO1C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Escape a string for safe interpolation into HTML text/attribute context. */
|
|
2
|
+
export function esc(value) {
|
|
3
|
+
return String(value ?? "")
|
|
4
|
+
.replace(/&/g, "&")
|
|
5
|
+
.replace(/</g, "<")
|
|
6
|
+
.replace(/>/g, ">")
|
|
7
|
+
.replace(/"/g, """)
|
|
8
|
+
.replace(/'/g, "'");
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=escape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escape.js","sourceRoot":"","sources":["../src/escape.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,UAAU,GAAG,CAAC,KAAc;IAChC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,mFAAmF;AACnF,eAAO,MAAM,aAAa,EAAE,YAiD3B,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Representative sample report — drives the standalone browser demo and tests. */
|
|
2
|
+
export const SAMPLE_REPORT = {
|
|
3
|
+
meta: {
|
|
4
|
+
title: "Codebase Health Studio",
|
|
5
|
+
subtitle: "React 18 · CRA",
|
|
6
|
+
target: "acme-dashboard",
|
|
7
|
+
generatedAt: "2026-06-27",
|
|
8
|
+
tool: "legacy-analyzer",
|
|
9
|
+
},
|
|
10
|
+
score: 62,
|
|
11
|
+
totalIssues: 18,
|
|
12
|
+
chips: [
|
|
13
|
+
{ label: "Framework", value: "Create React App" },
|
|
14
|
+
{ label: "React", value: "18.2.0" },
|
|
15
|
+
{ label: "Language", value: "JavaScript" },
|
|
16
|
+
{ label: "Components", value: "94" },
|
|
17
|
+
],
|
|
18
|
+
topActions: [
|
|
19
|
+
{ id: "fix-god", label: "Split the 3 god components", kind: "tool", tool: "component-fixer", params: { target: "components" }, fallback: "Refactor Dashboard.jsx, Settings.jsx and Reports.jsx into smaller components." },
|
|
20
|
+
{ id: "plan", label: "Generate full refactor plan", kind: "tool", tool: "generate-refactor-plan", params: { path: "." }, fallback: "Generate a refactor plan for acme-dashboard." },
|
|
21
|
+
{ id: "migrate-ts", label: "Migrate the API layer to TypeScript", kind: "prompt", prompt: "Convert src/api/*.js to TypeScript with typed responses and a centralised client." },
|
|
22
|
+
],
|
|
23
|
+
categories: [
|
|
24
|
+
{ id: "components", name: "Components", score: 48, status: "warn", summary: "3 god components over 300 lines; 11 with mixed responsibilities.", issueCount: 6, details: [{ label: "Total", value: "94" }, { label: "Large", value: "3" }] },
|
|
25
|
+
{ id: "state", name: "State", score: 55, status: "warn", summary: "Redux without normalisation; derived state stored in the store.", issueCount: 3 },
|
|
26
|
+
{ id: "api", name: "API layer", score: 40, status: "bad", summary: "Scattered axios calls, 4 duplicated endpoints, no central client.", issueCount: 4 },
|
|
27
|
+
{ id: "routing", name: "Routing", score: 78, status: "good", summary: "react-router v6, nested routes, but no lazy loading.", issueCount: 1 },
|
|
28
|
+
{ id: "styling", name: "Styling", score: 60, status: "warn", summary: "Mixed CSS + styled-components; 23 hardcoded colours.", issueCount: 2 },
|
|
29
|
+
{ id: "deps", name: "Dependencies", score: 84, status: "good", summary: "2 unused deps; moment.js inflating the bundle.", issueCount: 2 },
|
|
30
|
+
],
|
|
31
|
+
issues: [
|
|
32
|
+
{ id: "i1", category: "components", severity: "critical", title: "God component: Dashboard.jsx (612 lines)", description: "Dashboard.jsx mixes data fetching, layout, and 4 unrelated widgets. It is the single largest re-render hotspot.", file: "src/pages/Dashboard.jsx", meta: [{ label: "Lines", value: "612" }, { label: "Responsibilities", value: "5" }], actions: [{ id: "fix-i1", label: "Split with component-fixer", kind: "tool", tool: "component-fixer", params: { file: "src/pages/Dashboard.jsx" }, fallback: "Split src/pages/Dashboard.jsx into smaller components." }, { id: "explain-i1", label: "Explain the risk", kind: "prompt", prompt: "Explain why a 612-line god component hurts maintainability and performance in React." }] },
|
|
33
|
+
{ id: "i2", category: "api", severity: "critical", title: "Duplicated endpoint: GET /users called 4 ways", description: "Four components call GET /users with slightly different axios config — no shared client or cache.", file: "src/api/", meta: [{ label: "Call sites", value: "4" }], actions: [{ id: "fix-i2", label: "Centralise the API client", kind: "prompt", prompt: "Create a centralised typed axios client and replace the 4 duplicate GET /users call sites." }] },
|
|
34
|
+
{ id: "i3", category: "components", severity: "high", title: "God component: Settings.jsx (438 lines)", file: "src/pages/Settings.jsx", meta: [{ label: "Lines", value: "438" }], actions: [{ id: "fix-i3", label: "Split with component-fixer", kind: "tool", tool: "component-fixer", params: { file: "src/pages/Settings.jsx" }, fallback: "Split src/pages/Settings.jsx." }] },
|
|
35
|
+
{ id: "i4", category: "api", severity: "high", title: "No central HTTP client — axios imported in 19 files", file: "src/", actions: [] },
|
|
36
|
+
{ id: "i5", category: "state", severity: "high", title: "Derived state stored in Redux (totals recomputed on every action)", file: "src/store/cartSlice.js", actions: [{ id: "explain-i5", label: "How to fix", kind: "prompt", prompt: "Show how to replace derived state in Redux with reselect selectors." }] },
|
|
37
|
+
{ id: "i6", category: "deps", severity: "high", title: "moment.js adds ~230KB — replace with date-fns or Temporal", file: "package.json", meta: [{ label: "Bundle impact", value: "~230KB" }], actions: [] },
|
|
38
|
+
{ id: "i7", category: "components", severity: "medium", title: "Prop drilling 4 levels deep for `user`", file: "src/pages/Reports.jsx", actions: [] },
|
|
39
|
+
{ id: "i8", category: "styling", severity: "medium", title: "23 hardcoded hex colours outside the token system", file: "src/styles/", actions: [] },
|
|
40
|
+
{ id: "i9", category: "state", severity: "medium", title: "No memoised selectors — components re-render on unrelated store changes", file: "src/store/", actions: [] },
|
|
41
|
+
{ id: "i10", category: "api", severity: "medium", title: "Errors swallowed — 11 axios calls have no .catch", file: "src/api/", actions: [] },
|
|
42
|
+
{ id: "i11", category: "components", severity: "medium", title: "List rendered with array index as key", file: "src/components/Table.jsx", actions: [] },
|
|
43
|
+
{ id: "i12", category: "routing", severity: "medium", title: "No route-level code splitting (React.lazy)", file: "src/App.jsx", actions: [] },
|
|
44
|
+
{ id: "i13", category: "deps", severity: "low", title: "2 unused dependencies in package.json", file: "package.json", actions: [] },
|
|
45
|
+
{ id: "i14", category: "styling", severity: "low", title: "Duplicate utility classes across 6 CSS files", file: "src/styles/", actions: [] },
|
|
46
|
+
{ id: "i15", category: "components", severity: "low", title: "12 components missing displayName", file: "src/components/", actions: [] },
|
|
47
|
+
{ id: "i16", category: "api", severity: "low", title: "Base URL hardcoded instead of env var", file: "src/api/config.js", actions: [] },
|
|
48
|
+
{ id: "i17", category: "components", severity: "low", title: "Inline arrow functions in 31 render paths", file: "src/", actions: [] },
|
|
49
|
+
{ id: "i18", category: "state", severity: "low", title: "Local component state duplicates Redux store data", file: "src/pages/Profile.jsx", actions: [] },
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=fixture.js.map
|