@dreamtree-org/twreact-ui 1.1.50 → 1.1.51

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
@@ -47,29 +47,29 @@ function App() {
47
47
  ## AI agents (MCP server)
48
48
 
49
49
  If you build UI with an AI coding agent (Claude Code, Cursor, …), wire up the
50
- companion **[`@dreamtree-org/twreact-ui-mcp`](https://www.npmjs.com/package/@dreamtree-org/twreact-ui-mcp)**
51
- [Model Context Protocol](https://modelcontextprotocol.io) server. It gives the
52
- agent live, queryable access to this library — `list_components`,
53
- `get_component` (authoritative props/variants/sizes/examples per component),
54
- `search_components`, the per-component docs as resources, and a `compose_ui`
55
- prompt — so it stops guessing prop names.
50
+ **[Model Context Protocol](https://modelcontextprotocol.io) server bundled in
51
+ this package**. It gives the agent live, queryable access to the library —
52
+ `list_components`, `get_component` (authoritative props/variants/sizes/examples
53
+ per component), `search_components`, the per-component docs as resources, and a
54
+ `compose_ui` prompt so it stops guessing prop names.
56
55
 
57
- Add it to your MCP client config (no install needed `npx` fetches it):
56
+ Add it to your MCP client config (no extra install — it ships with the
57
+ library):
58
58
 
59
59
  ```jsonc
60
60
  {
61
61
  "mcpServers": {
62
62
  "dreamtree-ui": {
63
63
  "command": "npx",
64
- "args": ["-y", "@dreamtree-org/twreact-ui-mcp"]
64
+ "args": ["-y", "@dreamtree-org/twreact-ui", "mcp"]
65
65
  }
66
66
  }
67
67
  }
68
68
  ```
69
69
 
70
- The catalog is a build-time snapshot baked into that package, so the server is
71
- self-contained (no source or network needed at runtime). Upgrade it alongside
72
- `@dreamtree-org/twreact-ui` to keep the served contracts current.
70
+ The catalog is a build-time snapshot baked into the package, so the server is
71
+ self-contained (no source or network needed at runtime) and always matches the
72
+ library version you installed.
73
73
 
74
74
  > The MCP server also serves the skill below as the `dreamtree://skill`
75
75
  > resource, so MCP clients get it with **zero install**.
@@ -65,16 +65,16 @@ module.exports = {
65
65
 
66
66
  ## Live prop contracts via MCP (prefer this over guessing)
67
67
 
68
- A companion **MCP server** ships the authoritative prop contracts for this
69
- library. If your client supports the Model Context Protocol, wire it up and
70
- **query it instead of guessing prop names**:
68
+ An **MCP server bundled in this package** serves the authoritative prop
69
+ contracts for this library. If your client supports the Model Context Protocol,
70
+ wire it up and **query it instead of guessing prop names**:
71
71
 
72
72
  ```jsonc
73
73
  {
74
74
  "mcpServers": {
75
75
  "dreamtree-ui": {
76
76
  "command": "npx",
77
- "args": ["-y", "@dreamtree-org/twreact-ui-mcp"]
77
+ "args": ["-y", "@dreamtree-org/twreact-ui", "mcp"]
78
78
  }
79
79
  }
80
80
  }
@@ -88,8 +88,8 @@ library. If your client supports the Model Context Protocol, wire it up and
88
88
 
89
89
  Resources: `dreamtree://skill` (this guide) and `dreamtree://docs/<Component>`.
90
90
  Prompt: `compose_ui`. The catalog is a snapshot baked into the package, so it is
91
- self-contained; upgrade `@dreamtree-org/twreact-ui-mcp` alongside the library.
92
- The lists below are the fallback when the MCP server isn't connected.
91
+ self-contained and always matches the installed library version. The lists below
92
+ are the fallback when the MCP server isn't connected.
93
93
 
94
94
  ## Public surface
95
95
 
package/bin/cli.mjs CHANGED
@@ -55,18 +55,21 @@ function printHelp() {
55
55
  process.stdout.write(
56
56
  `${PKG} CLI (v${VERSION})\n\n` +
57
57
  `Usage:\n` +
58
- ` npx ${PKG} init --ai <provider> [--out <path>] [--force]\n\n` +
59
- `Installs the AI-assistant skill (ai-skills/dreamtree-ui.md) so your coding\n` +
60
- `agent knows this library's components, conventions, and wiring.\n\n` +
61
- `Options:\n` +
58
+ ` npx ${PKG} init --ai <provider> [--out <path>] [--force]\n` +
59
+ ` npx ${PKG} mcp\n\n` +
60
+ `Commands:\n` +
61
+ ` init Install the AI-assistant skill (ai-skills/dreamtree-ui.md) so your\n` +
62
+ ` coding agent knows this library's components, conventions, wiring.\n` +
63
+ ` mcp Run the Model Context Protocol server over stdio — gives an MCP\n` +
64
+ ` client live, queryable access to the library (list_components,\n` +
65
+ ` get_component, search_components + docs + a compose_ui prompt).\n` +
66
+ ` Wire it as: { "command": "npx", "args": ["-y", "${PKG}", "mcp"] }\n\n` +
67
+ `init options:\n` +
62
68
  ` --ai <provider> Target assistant: ${providers} (default: claude)\n` +
63
69
  ` --out <path> Write to this path instead of the provider default\n` +
64
70
  ` --force Overwrite the destination if it already exists\n` +
65
71
  ` -h, --help Show this help\n` +
66
- ` -v, --version Print the version\n\n` +
67
- `Tip: if your client speaks MCP, you don't need this — wire up\n` +
68
- `@dreamtree-org/twreact-ui-mcp and the skill is served as the\n` +
69
- `dreamtree://skill resource (zero install).\n`
72
+ ` -v, --version Print the version\n`
70
73
  );
71
74
  }
72
75
 
@@ -114,15 +117,34 @@ function runInit(args) {
114
117
  );
115
118
  }
116
119
 
117
- function main() {
120
+ // Boot the bundled MCP server over stdio. The SDK + server are lazy-imported
121
+ // here so plain `init` carries no dependency cost. stdout is the MCP protocol
122
+ // channel, so this path must only ever log to stderr. The shipped server reads
123
+ // the frozen snapshot (mcp/catalog-snapshot.mjs), so it needs no src/ or doc/.
124
+ async function runMcp() {
125
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
126
+ const { createServer } = await import("../mcp/server-core.mjs");
127
+ const catalog = await import("../mcp/catalog-snapshot.mjs");
128
+ const server = createServer({ version: VERSION, catalog });
129
+ await server.connect(new StdioServerTransport());
130
+ process.stderr.write(
131
+ `[dreamtree-ui mcp] serving ${PKG} — ${catalog.buildCatalog().counts.components} components\n`
132
+ );
133
+ }
134
+
135
+ async function main() {
118
136
  const args = parseArgs(process.argv.slice(2));
119
137
  if (args.version) return process.stdout.write(`${VERSION}\n`);
120
138
  if (args.help || args._.length === 0) return printHelp();
121
139
 
122
140
  const cmd = args._[0];
123
141
  if (cmd === "init") return runInit(args);
142
+ if (cmd === "mcp") return runMcp();
124
143
 
125
144
  fail(`unknown command "${cmd}". Run \`npx ${PKG} --help\`.`);
126
145
  }
127
146
 
128
- main();
147
+ main().catch((err) => {
148
+ process.stderr.write(`error: ${err?.stack || err}\n`);
149
+ process.exit(1);
150
+ });
package/mcp/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # dreamtree-ui MCP server
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes
4
+ `@dreamtree-org/twreact-ui` to AI agents: a coding agent can **discover the
5
+ catalog, fetch authoritative prop contracts, search by capability, and read the
6
+ docs — live**, instead of guessing prop names or copy-pasting from memory.
7
+
8
+ This is the concrete delivery of the project's **AI-native moat** (roadmap issue
9
+ [#61](https://github.com/DreamtreeTech/dreamtree-ui/issues/61)): something a
10
+ copy-paste generator like shadcn/ui structurally cannot offer.
11
+
12
+ ## What it serves
13
+
14
+ **Tools**
15
+ | Tool | Args | Returns |
16
+ | --- | --- | --- |
17
+ | `list_components` | — | The full grouped catalog (core / feedback / navigation / utility + hooks + utils) with descriptions. |
18
+ | `get_component` | `name` | One component's spec: import line, props (name/type/default/description), variants, sizes, examples, family exports, a11y + convention notes. Accepts a family-export name (`useToast` → `Toast`). |
19
+ | `search_components` | `query` | Ranked matches across names, family exports, descriptions, prop names, and examples. |
20
+
21
+ **Resources**
22
+ - `dreamtree://skill` — the consumer AI usage guide (`ai-skills/dreamtree-ui.md`).
23
+ - `dreamtree://docs/<Component>` — the per-component manual (`doc/<Component>.md`), one per documented component.
24
+
25
+ **Prompt**
26
+ - `compose_ui` (optional `task` arg) — primes an agent to build UI with the library correctly (vocabulary, providers, the get_component-before-use rule).
27
+
28
+ ## Single source of truth
29
+
30
+ The catalog (`catalog.mjs`) is **derived, not hand-maintained**: it reads the
31
+ public exports from `src/index.js` (names + group via import path) and the
32
+ props/examples from `doc/<Component>.md`. Keep those accurate and the MCP server
33
+ stays truthful automatically. There is no duplicate component list to drift.
34
+
35
+ ## Run it
36
+
37
+ ```bash
38
+ # from the repo root
39
+ npm run mcp # node mcp/server.mjs (stdio transport)
40
+ npm run mcp:smoke # end-to-end check: boots the server, exercises every tool/resource/prompt
41
+ ```
42
+
43
+ It speaks MCP over **stdio** — logs go to stderr, the protocol uses stdout.
44
+
45
+ ## Use it from Claude Code (this repo)
46
+
47
+ Already wired in [`.mcp.json`](../.mcp.json):
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "dreamtree-ui": { "command": "node", "args": ["mcp/server.mjs"] }
53
+ }
54
+ }
55
+ ```
56
+
57
+ Open this repo in Claude Code and the `dreamtree-ui` tools/resources are
58
+ available. The companion Claude Code skill at
59
+ `.claude/skills/dreamtree-ui/SKILL.md` tells the agent to query these tools
60
+ before composing UI.
61
+
62
+ ## Use it from another MCP client
63
+
64
+ Point any MCP client at `node /path/to/dreamtree-ui/mcp/server.mjs`. It needs
65
+ this repo present (the catalog reads `src/index.js` + `doc/`).
66
+
67
+ ## Repo-local vs shipped — both in this package
68
+
69
+ - **Live server (`mcp/server.mjs`)** — reads `src/index.js` + `doc/*.md` live
70
+ via `mcp/catalog.mjs`. It's what `.mcp.json` and `npm run mcp` run.
71
+ - **Shipped server** — consumers run `npx -y @dreamtree-org/twreact-ui mcp`
72
+ (the `mcp` subcommand of the library's bin, `bin/cli.mjs`). Since `src/` and
73
+ `doc/` aren't in the published tarball, it reads a frozen build-time
74
+ **snapshot** (`mcp/catalog.snapshot.json`) via `mcp/catalog-snapshot.mjs`.
75
+
76
+ Both import the same handlers (`server-core.mjs`) and pure queries
77
+ (`catalog-core.mjs`) — single source of truth, nothing copied.
78
+ `npm run mcp:snapshot` regenerates the one generated artifact,
79
+ `mcp/catalog.snapshot.json`. The MCP SDK is a runtime `dependency` of the
80
+ library, lazy-imported only by the `mcp` subcommand (never in a consumer's app
81
+ bundle). See
82
+ [`docs/agents/11-mcp-sync.md §7`](../docs/agents/11-mcp-sync.md#7-consumer-distribution--bundled-in-the-main-package)
83
+ (delivered under #74 / the moat in #61).
@@ -0,0 +1,44 @@
1
+ // Pure catalog queries — no filesystem, no data source.
2
+ // ---------------------------------------------------------------------------
3
+ // These operate on an already-built catalog object (the shape returned by
4
+ // buildCatalog()), so they are identical whether the catalog came from the
5
+ // live source (mcp/catalog.mjs, reads src/ + doc/) or the frozen snapshot
6
+ // (mcp/catalog-snapshot.mjs, reads mcp/catalog.snapshot.json). Both servers
7
+ // import this file directly — no copying — which is what lets the repo-local
8
+ // `mcp/server.mjs` and the shipped `twreact-ui mcp` subcommand share one
9
+ // implementation.
10
+
11
+ function allEntries(catalog) {
12
+ return [...catalog.components, ...catalog.hooks, ...catalog.utils, ...catalog.store];
13
+ }
14
+
15
+ // Resolve a name to a single catalog entry: exact match, then family-export
16
+ // (e.g. "useToast" → "Toast"), then case-insensitive. Returns null if none.
17
+ export function findComponentIn(catalog, name) {
18
+ const all = allEntries(catalog);
19
+ const lower = String(name || "").toLowerCase();
20
+ return (
21
+ all.find((x) => x.name === name) ||
22
+ all.find((x) => x.familyExports.includes(name)) ||
23
+ all.find((x) => x.name.toLowerCase() === lower) ||
24
+ null
25
+ );
26
+ }
27
+
28
+ // Ranked keyword search across names, family exports, descriptions, prop
29
+ // names, and examples. Returns entries with a `_score`, highest first.
30
+ export function searchCatalogIn(catalog, query) {
31
+ const q = String(query || "").toLowerCase().trim();
32
+ if (!q) return [];
33
+ const scored = [];
34
+ for (const x of allEntries(catalog)) {
35
+ let score = 0;
36
+ if (x.name.toLowerCase().includes(q)) score += 10;
37
+ if (x.familyExports.some((f) => f.toLowerCase().includes(q))) score += 6;
38
+ if (x.description.toLowerCase().includes(q)) score += 3;
39
+ if (x.props.some((p) => p.prop.toLowerCase().includes(q))) score += 2;
40
+ if (x.examples.toLowerCase().includes(q)) score += 1;
41
+ if (score > 0) scored.push({ ...x, _score: score });
42
+ }
43
+ return scored.sort((a, b) => b._score - a._score);
44
+ }
@@ -0,0 +1,42 @@
1
+ // Snapshot-backed catalog — what the SHIPPED `twreact-ui mcp` server reads.
2
+ // ---------------------------------------------------------------------------
3
+ // Exposes the SAME surface as the live mcp/catalog.mjs
4
+ // buildCatalog, findComponent, searchCatalog, listDocNames, readDoc, readSkill
5
+ // so the shared server core (mcp/server-core.mjs) is agnostic to the source.
6
+ // Here the data is a frozen snapshot (mcp/catalog.snapshot.json, generated by
7
+ // scripts/gen-mcp-snapshot.mjs) — no src/ or doc/ needed at runtime, so it
8
+ // works after a plain `npm install @dreamtree-org/twreact-ui`. The pure query
9
+ // helpers are shared verbatim with the live server (./catalog-core.mjs); no
10
+ // copying — both the live and shipped servers import the same files in this
11
+ // package.
12
+ import { readFileSync } from "node:fs";
13
+ import { fileURLToPath } from "node:url";
14
+ import { dirname, join } from "node:path";
15
+ import { findComponentIn, searchCatalogIn } from "./catalog-core.mjs";
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const snapshot = JSON.parse(readFileSync(join(__dirname, "catalog.snapshot.json"), "utf8"));
19
+
20
+ export function buildCatalog() {
21
+ return snapshot.catalog;
22
+ }
23
+
24
+ export function findComponent(name) {
25
+ return findComponentIn(snapshot.catalog, name);
26
+ }
27
+
28
+ export function searchCatalog(query) {
29
+ return searchCatalogIn(snapshot.catalog, query);
30
+ }
31
+
32
+ export function listDocNames() {
33
+ return Object.keys(snapshot.docs);
34
+ }
35
+
36
+ export function readDoc(name) {
37
+ return snapshot.docs[name] ?? null;
38
+ }
39
+
40
+ export function readSkill() {
41
+ return snapshot.skill ?? null;
42
+ }