@theme-registry/refract-mcp 0.1.1 → 0.1.2
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 +5 -0
- package/dist/server.js +38 -7
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ against it, so the agent asks about *your* theme without ever resending it. It r
|
|
|
22
22
|
| `checkContrast` | Do the theme's colour pairings pass WCAG-2 (+ advisory APCA)? |
|
|
23
23
|
| `validateTheme` | Is a candidate theme valid on every configured target? — returns **every** problem at once (collect-all), per target. |
|
|
24
24
|
| `diffTheme` | What's the blast radius of a candidate edit — which tokens moved, classes changed, pairings crossed a threshold, targets stopped building? |
|
|
25
|
+
| `reload` | Reload the project's theme config from disk (also happens automatically on change). |
|
|
25
26
|
|
|
26
27
|
`diffTheme` is the plan-then-apply guardrail: pass a **candidate edit** and it builds it against the
|
|
27
28
|
project's real adapters and reports the blast radius *before* the agent writes — the claim a token file
|
|
@@ -31,6 +32,10 @@ a generic check would miss. `getClass` / `resolveToken` read the real emitted na
|
|
|
31
32
|
what ships. The query tools take no
|
|
32
33
|
`theme` argument — they read the loaded project theme.
|
|
33
34
|
|
|
35
|
+
Note: over MCP, `getClass` takes **named** arguments — `{ subsystem, group, variant }` (a JSON object,
|
|
36
|
+
as the tool schema requires) — whereas the runtime `theme.getClass(subsystem, group, variant)` takes the
|
|
37
|
+
same three **positionally**. Same identifiers, different call shape for the two surfaces.
|
|
38
|
+
|
|
34
39
|
## Architecture
|
|
35
40
|
|
|
36
41
|
Pure tools (`src/tools.ts`, unit-tested) operate on a built `Theme` the server holds; `callTool`
|
package/dist/server.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync, watch } from 'node:fs';
|
|
3
|
-
import { dirname } from 'node:path';
|
|
2
|
+
import { readFileSync, watch, realpathSync } from 'node:fs';
|
|
3
|
+
import { basename, dirname } from 'node:path';
|
|
4
4
|
import { parseArgs } from 'node:util';
|
|
5
5
|
import { argv } from 'node:process';
|
|
6
|
-
import {
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
8
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
9
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -443,9 +443,40 @@ async function start(args) {
|
|
|
443
443
|
transport.onclose = () => process.exit(0);
|
|
444
444
|
await createServer().connect(transport);
|
|
445
445
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
446
|
+
const realpathOr = (p) => {
|
|
447
|
+
try {
|
|
448
|
+
return realpathSync(p);
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
return p;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
/**
|
|
455
|
+
* Is the launched script (`argv[1]`) this very module, resolving symlinks on BOTH sides? The published
|
|
456
|
+
* bin runs through a `node_modules/.bin/refract-mcp` symlink, so a raw path/URL compare misses (the
|
|
457
|
+
* symlink path ≠ the real dist path) and the server silently exits 0 — a hostile failure mode for
|
|
458
|
+
* something agents auto-spawn. Resolving through realpath fixes it. Pure + injectable `real` so the
|
|
459
|
+
* decision is unit-testable without spawning a process.
|
|
460
|
+
*/
|
|
461
|
+
function isBinEntry(invokedAs, selfPath, real = realpathOr) {
|
|
462
|
+
return invokedAs !== undefined && real(invokedAs) === real(selfPath);
|
|
463
|
+
}
|
|
464
|
+
// Auto-start only when invoked as the bin entry. If the launched file IS this file by name but its path
|
|
465
|
+
// won't resolve to this module, say so loudly instead of no-op. A genuine module import (argv[1] is the
|
|
466
|
+
// test runner, a different basename) stays silent.
|
|
467
|
+
{
|
|
468
|
+
const invokedAs = argv[1];
|
|
469
|
+
const selfPath = fileURLToPath(import.meta.url);
|
|
470
|
+
if (isBinEntry(invokedAs, selfPath)) {
|
|
471
|
+
start(argv.slice(2)).catch((e) => {
|
|
472
|
+
process.stderr.write(`refract-mcp: failed to start — ${e.message}\n`);
|
|
473
|
+
process.exit(1);
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else if (invokedAs && basename(invokedAs) === basename(selfPath)) {
|
|
477
|
+
process.stderr.write(`refract-mcp: launched as "${invokedAs}" but that path doesn't resolve to this module (${selfPath}); ` +
|
|
478
|
+
`not auto-starting. Use the \`refract-mcp\` bin or \`node ${selfPath}\`.\n`);
|
|
479
|
+
}
|
|
449
480
|
}
|
|
450
481
|
|
|
451
|
-
export { callTool, createServer, guideFiles, loadTheme, serverVersion, start };
|
|
482
|
+
export { callTool, createServer, guideFiles, isBinEntry, loadTheme, serverVersion, 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.2",
|
|
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,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@theme-registry/refract": "^0.1.
|
|
28
|
+
"@theme-registry/refract": "^0.1.2"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"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.2"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "rollup -c",
|