@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 +11 -11
- package/ai-skills/dreamtree-ui.md +6 -6
- package/bin/cli.mjs +32 -10
- package/mcp/README.md +83 -0
- package/mcp/catalog-core.mjs +44 -0
- package/mcp/catalog-snapshot.mjs +42 -0
- package/mcp/catalog.snapshot.json +3142 -0
- package/mcp/server-core.mjs +234 -0
- package/package.json +8 -6
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
`
|
|
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
|
|
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
|
|
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
|
|
71
|
-
self-contained (no source or network needed at runtime)
|
|
72
|
-
|
|
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
|
-
|
|
69
|
-
library. If your client supports the Model Context Protocol,
|
|
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
|
|
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
|
|
92
|
-
|
|
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
|
|
59
|
-
`
|
|
60
|
-
`
|
|
61
|
-
`
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|