@webjsdev/mcp 0.1.9 → 0.1.10

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 CHANGED
@@ -35,6 +35,10 @@ subcommand) delegates to this same server, so both routes run identical code.
35
35
  and `prompts` (the recipes as guided workflows).
36
36
  - **`source` tool**: reads the framework's own no-build source from
37
37
  `node_modules/@webjsdev/*/src` (read-only, traversal-guarded).
38
+ - **`ui` tool**: the `@webjsdev/ui` kit inventory (no args) or one component's
39
+ helper signatures, paste-ready structural example, a11y header, and deps (pass
40
+ `name`). Kit-scoped (not `appDir`-scoped); shares one projector with
41
+ `webjsui view`.
38
42
 
39
43
  The docs corpus is bundled into the package at `prepack`, so `npx @webjsdev/mcp`
40
44
  is self-contained; in the monorepo it falls back to the live repo-root docs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webjsdev/mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module",
5
5
  "description": "The webjs Model Context Protocol server: live app introspection (routes / actions / components / check) plus the framework knowledge layer (docs, recipes, source) for AI coding agents",
6
6
  "bin": {
@@ -25,7 +25,8 @@
25
25
  "resources"
26
26
  ],
27
27
  "dependencies": {
28
- "@webjsdev/server": "^0.8.0"
28
+ "@webjsdev/server": "^0.8.0",
29
+ "@webjsdev/ui": "^0.3.9"
29
30
  },
30
31
  "publishConfig": {
31
32
  "access": "public"
@@ -133,7 +133,7 @@ Self-check: `page.ts` / `layout.ts` should NOT appear in the network tab or the
133
133
 
134
134
  ## Framework source: where to find it
135
135
 
136
- Plain JS with JSDoc lives in `node_modules/@webjsdev/` (`core/`, `server/`, `cli/`, `mcp/`, `intellisense/`, `ui/`); what you read is what runs. Starting points: SSR `@webjsdev/server/src/ssr.js`, client hydration `@webjsdev/core/src/render-client.js`, client router `@webjsdev/core/src/router-client.js`, convention rules `@webjsdev/server/src/check.js`. For UI debugging use the Playwright MCP server; for live introspection the scaffold wires the read-only `@webjsdev/mcp` server (`npx @webjsdev/mcp`, also reachable as `webjs mcp`): `list_routes`, `list_actions`, `list_components`, `check`, plus a knowledge layer (docs / recipes / framework source).
136
+ Plain JS with JSDoc lives in `node_modules/@webjsdev/` (`core/`, `server/`, `cli/`, `mcp/`, `intellisense/`, `ui/`); what you read is what runs. Starting points: SSR `@webjsdev/server/src/ssr.js`, client hydration `@webjsdev/core/src/render-client.js`, client router `@webjsdev/core/src/router-client.js`, convention rules `@webjsdev/server/src/check.js`. For UI debugging use the Playwright MCP server; for live introspection the scaffold wires the read-only `@webjsdev/mcp` server (`npx @webjsdev/mcp`, also reachable as `webjs mcp`): `list_routes`, `list_actions`, `list_components`, `check`, `ui` (the `@webjsdev/ui` kit inventory + a component's helpers / paste-ready example / a11y header), plus a knowledge layer (docs / recipes / framework source).
137
137
 
138
138
  ---
139
139
 
@@ -463,7 +463,7 @@ webjs start [--port N] # prod server; source IS the runtime, plain H
463
463
  webjs test [--server] [--browser] [--watch]
464
464
  webjs check [--rules] [--json] # correctness validator (report-only, no autofix); --json for an agent loop
465
465
  webjs routes [--json] [--table] [--no-headers] # print the route table (path / owner file / methods, #975). Default tree; --json is byte-identical to the MCP list_routes tool; --no-headers drops the --table header for piping
466
- webjs mcp # read-only MCP: routes, actions (RPC hashes), components, check
466
+ webjs mcp # read-only MCP: routes, actions (RPC hashes), components, check, ui kit
467
467
  webjs doctor [--json] [--strict] # project-health checklist (incl. a framework-resolve check that warns when @webjsdev/core can't be resolved from the app dir, the fresh-worktree-without-node_modules trap #954; a page/layout elision advisory); non-zero exit on a hard fail. --json emits `{ results, summary }` (results is the DoctorResult[], each carrying a stable code); --strict also fails the exit on warnings (#975)
468
468
  webjs types # generate .webjs/routes.d.ts (typed Route union + per-route params, #258)
469
469
  webjs version # print the installed @webjsdev/cli version (also: webjs --version / -v, #975)
@@ -44,6 +44,7 @@ Classify the task first, then load the smallest useful reference set. Each refer
44
44
  | Tailwind, light-DOM tag-prefix rule, tokens, fixed headers, no-reflow layout | `references/styling.md` |
45
45
  | Client router, prefetch, frames, view transitions, Suspense streaming | `references/client-router-and-streaming.md` |
46
46
  | Optimistic UI for a user-facing mutation | `references/optimistic-ui.md` |
47
+ | The `@webjsdev/ui` component kit (a `components.json` is present): class helpers, tokens, `add` / `view`, the MCP `ui` tool | `references/ui-kit.md` |
47
48
  | TypeScript at runtime, erasable syntax, full-stack types | `references/typescript.md` |
48
49
  | Unit, browser, e2e tests, the `handle()` harness, Bun parity | `references/testing.md` |
49
50
  | Auth, caching, env vars, rate limit, file storage, the `webjs` config block | `references/built-ins.md` |
@@ -0,0 +1,68 @@
1
+ # The `@webjsdev/ui` component kit
2
+
3
+ Load this when the app has a `components.json` (it uses `@webjsdev/ui`, the
4
+ shadcn-style kit for WebJs). The source is copied into your repo (`components/ui/`),
5
+ so you own and edit it. Two tiers:
6
+
7
+ - **Tier 1, class helpers (23 components).** Pure functions returning Tailwind
8
+ class strings (`buttonClass({ variant })`, `cardClass()`), composed with
9
+ whatever native element you write. Reach for these instead of expanding
10
+ Tailwind by hand: the call site is a fraction of the tokens and the class list
11
+ cannot drift.
12
+ - **Tier 2, stateful custom elements (9 components).** `<ui-dialog>`, `<ui-tabs>`,
13
+ `<ui-dropdown-menu>`, and friends own their ARIA (focus trap, roving tabindex,
14
+ `aria-controls` / `inert`, live regions). Write the tag and the accessible
15
+ behaviour comes with it. Do NOT hand-roll these; the wiring is easy to get
16
+ subtly wrong.
17
+
18
+ ## The workflow: query for the structure, do not guess it
19
+
20
+ `add` copies a Tier-1 component's class helpers plus a lean header (what each
21
+ helper is, the accessibility obligations) and a one-line pointer. It does NOT
22
+ copy the worked structural example, because that example is guidance you consume
23
+ once while composing, not code that should sit in your repo. Get the full
24
+ paste-ready structure on demand:
25
+
26
+ - **MCP `ui` tool** (preferred when available): call `ui` with no args for the
27
+ kit inventory (each component's tier, helper signatures, npm deps); pass
28
+ `{ name: "accordion" }` for one component's helper signatures, the paste-ready
29
+ structural example, the accessibility header, and deps.
30
+ - **CLI**: `webjs ui list` (inventory), `webjs ui view <name>` (the projected
31
+ view plus the full source). Same data as the MCP tool (one shared projector).
32
+
33
+ So the loop is: `add` the component, then query `ui <name>` (MCP) or
34
+ `webjs ui view <name>` for the accessible structure, paste it, and fill it in.
35
+
36
+ ## Setup and resolution
37
+
38
+ - `webjs ui init` writes `components.json`, `lib/utils.ts`, and the CSS design
39
+ tokens the helpers render against (`--background`, `--foreground`,
40
+ `--destructive`, ...). It HARD-FAILS if the tokens cannot be written, so a
41
+ clean exit means the kit is styled. `add` self-heals the tokens if they go
42
+ missing.
43
+ - Resolution is LOCAL-FIRST: `init` / `add` / `list` / `view` read the registry
44
+ that ships inside the installed `@webjsdev/ui`, with no network. This pins you
45
+ to the installed version; run `webjs ui diff` to see where your local copies
46
+ drift from the upstream (that command alone compares against the live registry).
47
+
48
+ ## Inventory (run `webjs ui list` or the MCP `ui` tool for the authoritative, current set)
49
+
50
+ **Tier 1 (class helpers):** accordion, alert, aspect-ratio, avatar, badge,
51
+ breadcrumb, button, card, checkbox, collapsible, input, kbd, label,
52
+ native-select, pagination, popover, progress, radio-group, separator, skeleton,
53
+ switch, table, textarea.
54
+
55
+ **Tier 2 (custom elements, own their ARIA):** alert-dialog, dialog,
56
+ dropdown-menu, hover-card, sonner, tabs, tooltip, plus toggle and toggle-group
57
+ (these two register an element AND export a `*Class` helper).
58
+
59
+ ## Idioms
60
+
61
+ - A helper is a function, so compose it: `class=${buttonClass({ variant: 'outline' })}`.
62
+ The unquoted `${...}` is a normal `html` attribute hole.
63
+ - Tier-1 helpers assume the design tokens exist; if a component paints unstyled,
64
+ the tokens are missing (re-run `webjs ui init` or let `add` self-heal them).
65
+ - Custom elements are display-only-safe at SSR and hydrate in the browser, the
66
+ standard WebJs component model (`references/components.md`).
67
+
68
+ Full per-package reference lives in the installed `@webjsdev/ui/AGENTS.md`.
package/src/mcp.js CHANGED
@@ -81,6 +81,18 @@ const DOCS_SCHEMA = {
81
81
  required: [],
82
82
  };
83
83
 
84
+ /** `ui` lists the @webjsdev/ui kit, or details one component by `name`. */
85
+ const UI_SCHEMA = {
86
+ type: 'object',
87
+ properties: {
88
+ name: {
89
+ type: 'string',
90
+ description: 'A component name (e.g. button, accordion, dialog). Omit for the full kit inventory.',
91
+ },
92
+ },
93
+ required: [],
94
+ };
95
+
84
96
  /** `source` reads the framework source: a `path` to read, a `query` to grep, or a `package` to list. */
85
97
  const SOURCE_SCHEMA = {
86
98
  type: 'object',
@@ -127,6 +139,12 @@ const TOOL_DEFS = [
127
139
  'Read the FRAMEWORK authored source (webjs is buildless: node_modules/@webjsdev/*/src is the JSDoc source, run directly server-side; only the core browser bundle is built into dist/, which this skips). Pass `path` to read a file (e.g. server/src/ssr.js), `query` to grep the @webjsdev/* src trees, or no args to list the packages + entry points. Use when the docs do not answer something. Read-only.',
128
140
  inputSchema: SOURCE_SCHEMA,
129
141
  },
142
+ {
143
+ name: 'ui',
144
+ description:
145
+ 'The @webjsdev/ui component kit (shadcn-style, copied into your repo). No args returns the inventory (each component: tier, helper signatures, npm deps); pass `name` for one component: helper signatures, the paste-ready structural example, the a11y/description header, and deps. Use before hand-writing UI so you reach for a helper (e.g. buttonClass()) and the accessible structure instead of expanding Tailwind blind. Reads the kit, not your app. Read-only.',
146
+ inputSchema: UI_SCHEMA,
147
+ },
130
148
  {
131
149
  name: 'list_routes',
132
150
  description:
@@ -358,6 +376,50 @@ export function makeToolRunners(deps) {
358
376
  };
359
377
  }
360
378
 
379
+ /**
380
+ * Run the `ui` tool: the @webjsdev/ui kit inventory (no args) or one component's
381
+ * projection (helper signatures + paste-ready example + a11y/description header
382
+ * + deps). Projects the SHARED `@webjsdev/ui/registry/extract` leaf, the same
383
+ * one `webjsui view` renders, so the CLI and MCP cannot drift (#983, the #979
384
+ * shared-projector pattern). `uiDeps` is injected so tests need no real package.
385
+ *
386
+ * @param {{ uiInventory: Function, uiComponent: Function }} uiDeps
387
+ * @param {{ name?: string }} args
388
+ */
389
+ /**
390
+ * Resolve the `ui` tool's deps from the shared `@webjsdev/ui/registry/extract`
391
+ * leaf, GUARDED (#983): if `@webjsdev/ui` is absent or too old to carry the
392
+ * `./registry/extract` subpath (a cross-package version skew), only the `ui`
393
+ * tool degrades (its deps throw a clear "unavailable" error, surfaced as an
394
+ * isError tool result) while the rest of the server keeps working. An unguarded
395
+ * top-level import would instead reject during bootstrap and sink the whole
396
+ * server. `importer` is injected so the failure path is unit-testable.
397
+ *
398
+ * @param {() => Promise<{ uiInventory: Function, uiComponent: Function }>} importer
399
+ * @returns {Promise<{ uiInventory: Function, uiComponent: Function }>}
400
+ */
401
+ export async function loadUiDeps(importer) {
402
+ try {
403
+ const ui = await importer();
404
+ return { uiInventory: ui.uiInventory, uiComponent: ui.uiComponent };
405
+ } catch {
406
+ const unavailable = () => {
407
+ throw new Error('@webjsdev/ui is not available (install/upgrade @webjsdev/ui to use the ui tool)');
408
+ };
409
+ return { uiInventory: unavailable, uiComponent: unavailable };
410
+ }
411
+ }
412
+
413
+ export function runUiTool(uiDeps, args) {
414
+ const name = typeof args.name === 'string' && args.name ? args.name : null;
415
+ if (!name) return { inventory: uiDeps.uiInventory() };
416
+ const component = uiDeps.uiComponent(name);
417
+ if (!component) {
418
+ throw new Error(`Unknown component "${String(name)}". Call ui with no args for the inventory.`);
419
+ }
420
+ return component;
421
+ }
422
+
361
423
  /**
362
424
  * A JSON-RPC 2.0 result frame.
363
425
  * @param {string|number|null} id
@@ -454,6 +516,13 @@ export async function runMcpServer(opts) {
454
516
  };
455
517
  }
456
518
 
519
+ // The `ui` tool (#983): the @webjsdev/ui kit inventory / per-component
520
+ // projection, read from the shared `@webjsdev/ui/registry/extract` leaf.
521
+ // Injectable for tests; otherwise resolved (guarded) from the installed
522
+ // package. See {@link loadUiDeps} for why the import is guarded.
523
+ let uiDeps = opts.uiDeps;
524
+ if (!uiDeps) uiDeps = await loadUiDeps(() => import('@webjsdev/ui/registry/extract'));
525
+
457
526
  /** Write one JSON-RPC frame as a single line to stdout. */
458
527
  const send = (frame) => {
459
528
  stdout.write(JSON.stringify(frame) + '\n');
@@ -526,7 +595,9 @@ export async function runMcpServer(opts) {
526
595
  const args = params.arguments || {};
527
596
  // The knowledge tools route to the docs / source layer; they return text.
528
597
  const isKnowledgeTool = name === 'init' || name === 'docs' || name === 'source';
529
- if (!isKnowledgeTool && !runners[name]) {
598
+ // `ui` is kit-scoped (not app-scoped), so it is dispatched separately.
599
+ const isUiTool = name === 'ui';
600
+ if (!isKnowledgeTool && !isUiTool && !runners[name]) {
530
601
  return rpcError(id, -32602, `Unknown tool: ${String(name)}`);
531
602
  }
532
603
  const appDir = typeof args.appDir === 'string' && args.appDir ? args.appDir : cwd;
@@ -537,7 +608,9 @@ export async function runMcpServer(opts) {
537
608
  : name === 'docs'
538
609
  ? await searchDocs(docsDeps, args)
539
610
  : await runSourceTool(sourceDeps, args)
540
- : await runners[name](appDir);
611
+ : isUiTool
612
+ ? runUiTool(uiDeps, args)
613
+ : await runners[name](appDir);
541
614
  // Knowledge tools return a markdown string; introspection tools return
542
615
  // a JSON-serialisable object.
543
616
  const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);