@dreamtree-org/twreact-ui 1.1.50 → 1.1.52
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/dist/components/core/SpeechToText.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +176 -132
- package/dist/index.js +176 -132
- 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 +3148 -0
- package/mcp/server-core.mjs +234 -0
- package/package.json +8 -6
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
|
+
}
|