@theme-registry/refract-mcp 0.1.6 → 0.1.8
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 +8 -1
- package/dist/server.js +48 -14
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
It is **project-scoped**: it loads your `theme.config.(ts|js|mjs)` once at startup and serves queries
|
|
9
9
|
against it, so the agent asks about *your* theme without ever resending it. It reloads on change.
|
|
10
10
|
|
|
11
|
+
The reload watches the config's directory recursively — so a theme split across `./tokens/*.ts` reloads
|
|
12
|
+
too, not just an edit to the config itself. Hidden paths are never watched (that includes `.git`,
|
|
13
|
+
`.next`, `.turbo`, and the hidden `.mjs` files a `.ts` config's graph-compile emits beside your sources
|
|
14
|
+
and unlinks again), and neither are `node_modules` / `dist` / `out` / `coverage`. Reloads never overlap.
|
|
15
|
+
|
|
11
16
|
## Tools
|
|
12
17
|
|
|
13
18
|
| Tool | Answers |
|
|
@@ -62,7 +67,9 @@ claude mcp add refract -- node ./packages/refract-mcp/dist/server.js
|
|
|
62
67
|
```
|
|
63
68
|
|
|
64
69
|
The server auto-discovers `theme.config.(ts|js|mjs)` in the working directory; pass `--config <path>`
|
|
65
|
-
to point elsewhere. A `.ts` config needs the `typescript` optional peer (same as
|
|
70
|
+
to point elsewhere. A `.ts` config needs the `typescript` optional peer at **5.x** (same as
|
|
71
|
+
`refract build` — a bare `npm i -D typescript` now resolves to 7.x, which doesn't expose the compiler
|
|
72
|
+
API from its main entry). A `.mjs` or `.js` config never loads typescript at all.
|
|
66
73
|
|
|
67
74
|
For project scope, commit a `.mcp.json`:
|
|
68
75
|
|
package/dist/server.js
CHANGED
|
@@ -227,10 +227,25 @@ function diffHeld(state, candidateRaw) {
|
|
|
227
227
|
}
|
|
228
228
|
return { ok: validation.ok, diff, targets: validation.perTarget };
|
|
229
229
|
}
|
|
230
|
-
// Dirs never worth reloading on (vendored deps, build output
|
|
231
|
-
const WATCH_IGNORE = /(^|[/\\])(node_modules|dist|out
|
|
230
|
+
// Dirs never worth reloading on (vendored deps, build output). Skipped under the recursive watch.
|
|
231
|
+
const WATCH_IGNORE = /(^|[/\\])(node_modules|dist|out|coverage)([/\\]|$)/;
|
|
232
232
|
// Source files a theme graph is authored in — a change to any of them may change the built theme.
|
|
233
233
|
const WATCH_SOURCE = /\.(ts|mjs|cjs|js|json)$/;
|
|
234
|
+
/**
|
|
235
|
+
* Any hidden path segment. A theme graph is never authored in one, and this is the rule that keeps the
|
|
236
|
+
* server from waking on its own output: loading a `.ts` config graph-compiles it to hidden
|
|
237
|
+
* `.<base>.<pid>-<n>.mjs` files emitted **beside each compiled source** (adjacency is load-bearing —
|
|
238
|
+
* it's what keeps relative sibling specifiers resolvable), which are imported and then unlinked. Those
|
|
239
|
+
* writes and deletes match {@link WATCH_SOURCE}, so without this a load wakes the watcher, which
|
|
240
|
+
* reloads, which emits them again — an endless self-triggered loop with no user edit involved, churning
|
|
241
|
+
* the config's directory (visible in an editor as a file tree that never stops refreshing). Also covers
|
|
242
|
+
* `.git`, `.next`, `.turbo`, `.cache` and friends, whose build churn caused spurious reloads too.
|
|
243
|
+
*/
|
|
244
|
+
const WATCH_HIDDEN = /(^|[/\\])\./;
|
|
245
|
+
/** Is this watch event worth rebuilding the theme for? Pure, so the loop guard above is unit-testable. */
|
|
246
|
+
function shouldReload(filename) {
|
|
247
|
+
return WATCH_SOURCE.test(filename) && !WATCH_HIDDEN.test(filename) && !WATCH_IGNORE.test(filename);
|
|
248
|
+
}
|
|
234
249
|
/**
|
|
235
250
|
* Watch the config's directory **recursively** and rebuild on any source-file change — so a theme split
|
|
236
251
|
* across `./tokens/*.ts` (imported transitively by the config) reloads, not just an edit to the config
|
|
@@ -241,21 +256,40 @@ function watchConfig() {
|
|
|
241
256
|
if (!held)
|
|
242
257
|
return;
|
|
243
258
|
let timer;
|
|
259
|
+
// Second line of defence behind {@link WATCH_HIDDEN}: never overlap two loads, so anything a load
|
|
260
|
+
// writes can't start another one. Edits landing mid-load aren't lost — they re-arm the debounce once.
|
|
261
|
+
let reloading = false;
|
|
262
|
+
let pending = false;
|
|
263
|
+
const reload = () => {
|
|
264
|
+
if (reloading) {
|
|
265
|
+
pending = true;
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
reloading = true;
|
|
269
|
+
loadTheme(configPathArg)
|
|
270
|
+
.then((next) => {
|
|
271
|
+
held = next;
|
|
272
|
+
process.stderr.write(`refract-mcp: reloaded theme from ${next.path}\n`);
|
|
273
|
+
})
|
|
274
|
+
.catch((e) => process.stderr.write(`refract-mcp: reload failed (kept previous theme): ${e.message}\n`))
|
|
275
|
+
.finally(() => {
|
|
276
|
+
reloading = false;
|
|
277
|
+
if (pending) {
|
|
278
|
+
pending = false;
|
|
279
|
+
schedule();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
const schedule = () => {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
timer = setTimeout(reload, 150);
|
|
286
|
+
};
|
|
244
287
|
const onChange = (_event, filename) => {
|
|
245
288
|
if (!filename)
|
|
246
289
|
return;
|
|
247
|
-
|
|
248
|
-
if (WATCH_IGNORE.test(f) || !WATCH_SOURCE.test(f))
|
|
290
|
+
if (!shouldReload(filename.toString()))
|
|
249
291
|
return;
|
|
250
|
-
|
|
251
|
-
timer = setTimeout(() => {
|
|
252
|
-
loadTheme(configPathArg)
|
|
253
|
-
.then((next) => {
|
|
254
|
-
held = next;
|
|
255
|
-
process.stderr.write(`refract-mcp: reloaded theme from ${next.path}\n`);
|
|
256
|
-
})
|
|
257
|
-
.catch((e) => process.stderr.write(`refract-mcp: reload failed (kept previous theme): ${e.message}\n`));
|
|
258
|
-
}, 150);
|
|
292
|
+
schedule();
|
|
259
293
|
};
|
|
260
294
|
try {
|
|
261
295
|
const dir = dirname(held.path);
|
|
@@ -479,4 +513,4 @@ function isBinEntry(invokedAs, selfPath, real = realpathOr) {
|
|
|
479
513
|
}
|
|
480
514
|
}
|
|
481
515
|
|
|
482
|
-
export { callTool, createServer, guideFiles, isBinEntry, loadTheme, serverVersion, start };
|
|
516
|
+
export { callTool, createServer, guideFiles, isBinEntry, loadTheme, serverVersion, shouldReload, start };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theme-registry/refract-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Model Context Protocol server for refract — query + validate a project's theme from an AI agent.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Petyo Stoyanov",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@theme-registry/refract": "^0.1.
|
|
28
|
+
"@theme-registry/refract": "^0.1.8"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"typescript": "
|
|
31
|
+
"typescript": ">=5.0.0 <6"
|
|
32
32
|
},
|
|
33
33
|
"peerDependenciesMeta": {
|
|
34
34
|
"typescript": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"rollup": "^3.29.4",
|
|
43
43
|
"typescript": "^5.4.2",
|
|
44
44
|
"vitest": "^4.1.10",
|
|
45
|
-
"@theme-registry/refract-css": "^0.1.
|
|
45
|
+
"@theme-registry/refract-css": "^0.1.8"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "rollup -c",
|