@decantr/mcp-server 1.0.0-beta.3 → 1.0.0-beta.4
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/dist/index.js +120 -116
- package/package.json +8 -10
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -10,13 +10,12 @@ import {
|
|
|
10
10
|
|
|
11
11
|
// src/tools.ts
|
|
12
12
|
import { readFile } from "fs/promises";
|
|
13
|
-
import { join
|
|
13
|
+
import { join } from "path";
|
|
14
14
|
import { validateEssence, evaluateGuard } from "@decantr/essence-spec";
|
|
15
|
-
import {
|
|
15
|
+
import { resolvePatternPreset } from "@decantr/registry";
|
|
16
16
|
|
|
17
17
|
// src/helpers.ts
|
|
18
|
-
import {
|
|
19
|
-
import { join } from "path";
|
|
18
|
+
import { RegistryAPIClient } from "@decantr/registry";
|
|
20
19
|
var MAX_INPUT_LENGTH = 1e3;
|
|
21
20
|
function validateStringArg(args, field) {
|
|
22
21
|
const val = args[field];
|
|
@@ -28,18 +27,15 @@ function validateStringArg(args, field) {
|
|
|
28
27
|
}
|
|
29
28
|
return null;
|
|
30
29
|
}
|
|
31
|
-
var
|
|
32
|
-
function
|
|
33
|
-
if (!
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
_resolver = createResolver({
|
|
38
|
-
contentRoot: envRoot || bundledRoot,
|
|
39
|
-
overridePaths: envRoot ? [] : [npmRoot]
|
|
30
|
+
var _apiClient = null;
|
|
31
|
+
function getAPIClient() {
|
|
32
|
+
if (!_apiClient) {
|
|
33
|
+
_apiClient = new RegistryAPIClient({
|
|
34
|
+
baseUrl: process.env.DECANTR_API_URL || void 0,
|
|
35
|
+
apiKey: process.env.DECANTR_API_KEY || void 0
|
|
40
36
|
});
|
|
41
37
|
}
|
|
42
|
-
return
|
|
38
|
+
return _apiClient;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
// src/tools.ts
|
|
@@ -96,7 +92,8 @@ var TOOLS = [
|
|
|
96
92
|
type: "object",
|
|
97
93
|
properties: {
|
|
98
94
|
id: { type: "string", description: 'Pattern ID (e.g. "hero", "data-table", "kpi-grid")' },
|
|
99
|
-
preset: { type: "string", description: 'Optional preset name (e.g. "product", "content")' }
|
|
95
|
+
preset: { type: "string", description: 'Optional preset name (e.g. "product", "content")' },
|
|
96
|
+
namespace: { type: "string", description: 'Namespace (default: "@official")' }
|
|
100
97
|
},
|
|
101
98
|
required: ["id"]
|
|
102
99
|
},
|
|
@@ -109,7 +106,8 @@ var TOOLS = [
|
|
|
109
106
|
inputSchema: {
|
|
110
107
|
type: "object",
|
|
111
108
|
properties: {
|
|
112
|
-
id: { type: "string", description: 'Archetype ID (e.g. "saas-dashboard", "ecommerce")' }
|
|
109
|
+
id: { type: "string", description: 'Archetype ID (e.g. "saas-dashboard", "ecommerce")' },
|
|
110
|
+
namespace: { type: "string", description: 'Namespace (default: "@official")' }
|
|
113
111
|
},
|
|
114
112
|
required: ["id"]
|
|
115
113
|
},
|
|
@@ -122,7 +120,8 @@ var TOOLS = [
|
|
|
122
120
|
inputSchema: {
|
|
123
121
|
type: "object",
|
|
124
122
|
properties: {
|
|
125
|
-
id: { type: "string", description: 'Recipe ID (e.g. "auradecantism")' }
|
|
123
|
+
id: { type: "string", description: 'Recipe ID (e.g. "auradecantism")' },
|
|
124
|
+
namespace: { type: "string", description: 'Namespace (default: "@official")' }
|
|
126
125
|
},
|
|
127
126
|
required: ["id"]
|
|
128
127
|
},
|
|
@@ -135,7 +134,8 @@ var TOOLS = [
|
|
|
135
134
|
inputSchema: {
|
|
136
135
|
type: "object",
|
|
137
136
|
properties: {
|
|
138
|
-
id: { type: "string", description: 'Blueprint ID (e.g. "saas-dashboard", "ecommerce", "portfolio")' }
|
|
137
|
+
id: { type: "string", description: 'Blueprint ID (e.g. "saas-dashboard", "ecommerce", "portfolio")' },
|
|
138
|
+
namespace: { type: "string", description: 'Namespace (default: "@official")' }
|
|
139
139
|
},
|
|
140
140
|
required: ["id"]
|
|
141
141
|
},
|
|
@@ -189,9 +189,10 @@ var TOOLS = [
|
|
|
189
189
|
}
|
|
190
190
|
];
|
|
191
191
|
async function handleTool(name, args) {
|
|
192
|
+
const apiClient = getAPIClient();
|
|
192
193
|
switch (name) {
|
|
193
194
|
case "decantr_read_essence": {
|
|
194
|
-
const essencePath = args.path ||
|
|
195
|
+
const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
|
|
195
196
|
try {
|
|
196
197
|
const raw = await readFile(essencePath, "utf-8");
|
|
197
198
|
return JSON.parse(raw);
|
|
@@ -200,7 +201,7 @@ async function handleTool(name, args) {
|
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
case "decantr_validate": {
|
|
203
|
-
const essencePath = args.path ||
|
|
204
|
+
const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
|
|
204
205
|
let essence;
|
|
205
206
|
try {
|
|
206
207
|
essence = JSON.parse(await readFile(essencePath, "utf-8"));
|
|
@@ -221,16 +222,19 @@ async function handleTool(name, args) {
|
|
|
221
222
|
const err = validateStringArg(args, "query");
|
|
222
223
|
if (err) return { error: err };
|
|
223
224
|
try {
|
|
224
|
-
const
|
|
225
|
-
|
|
225
|
+
const response = await apiClient.search({
|
|
226
|
+
q: args.query,
|
|
227
|
+
type: args.type
|
|
228
|
+
});
|
|
226
229
|
return {
|
|
227
|
-
total:
|
|
228
|
-
results: results.map((r) => ({
|
|
230
|
+
total: response.total,
|
|
231
|
+
results: response.results.map((r) => ({
|
|
229
232
|
type: r.type,
|
|
230
|
-
id: r.
|
|
233
|
+
id: r.slug,
|
|
234
|
+
namespace: r.namespace,
|
|
231
235
|
name: r.name,
|
|
232
236
|
description: r.description,
|
|
233
|
-
install: `decantr
|
|
237
|
+
install: `decantr get ${r.type} ${r.slug}`
|
|
234
238
|
}))
|
|
235
239
|
};
|
|
236
240
|
} catch (e) {
|
|
@@ -240,116 +244,112 @@ async function handleTool(name, args) {
|
|
|
240
244
|
case "decantr_resolve_pattern": {
|
|
241
245
|
const err = validateStringArg(args, "id");
|
|
242
246
|
if (err) return { error: err };
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
const namespace = args.namespace || "@official";
|
|
248
|
+
try {
|
|
249
|
+
const pattern = await apiClient.getPattern(namespace, args.id);
|
|
250
|
+
const result = { found: true, ...pattern };
|
|
251
|
+
if (args.preset && typeof args.preset === "string") {
|
|
252
|
+
const preset = resolvePatternPreset(pattern, args.preset);
|
|
253
|
+
if (preset) result.resolvedPreset = preset;
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
} catch {
|
|
257
|
+
return { found: false, message: `Pattern "${args.id}" not found in ${namespace}.` };
|
|
252
258
|
}
|
|
253
|
-
return result;
|
|
254
259
|
}
|
|
255
260
|
case "decantr_resolve_archetype": {
|
|
256
261
|
const err = validateStringArg(args, "id");
|
|
257
262
|
if (err) return { error: err };
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
return { found:
|
|
263
|
+
const namespace = args.namespace || "@official";
|
|
264
|
+
try {
|
|
265
|
+
const archetype = await apiClient.getArchetype(namespace, args.id);
|
|
266
|
+
return { found: true, ...archetype };
|
|
267
|
+
} catch {
|
|
268
|
+
return { found: false, message: `Archetype "${args.id}" not found in ${namespace}.` };
|
|
262
269
|
}
|
|
263
|
-
return { found: true, ...resolved.item };
|
|
264
270
|
}
|
|
265
271
|
case "decantr_resolve_recipe": {
|
|
266
272
|
const err = validateStringArg(args, "id");
|
|
267
273
|
if (err) return { error: err };
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
return { found:
|
|
274
|
+
const namespace = args.namespace || "@official";
|
|
275
|
+
try {
|
|
276
|
+
const recipe = await apiClient.getRecipe(namespace, args.id);
|
|
277
|
+
return { found: true, ...recipe };
|
|
278
|
+
} catch {
|
|
279
|
+
return { found: false, message: `Recipe "${args.id}" not found in ${namespace}.` };
|
|
272
280
|
}
|
|
273
|
-
return { found: true, ...resolved.item };
|
|
274
281
|
}
|
|
275
282
|
case "decantr_resolve_blueprint": {
|
|
276
283
|
const err = validateStringArg(args, "id");
|
|
277
284
|
if (err) return { error: err };
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return { found:
|
|
285
|
+
const namespace = args.namespace || "@official";
|
|
286
|
+
try {
|
|
287
|
+
const blueprint = await apiClient.getBlueprint(namespace, args.id);
|
|
288
|
+
return { found: true, ...blueprint };
|
|
289
|
+
} catch {
|
|
290
|
+
return { found: false, message: `Blueprint "${args.id}" not found in ${namespace}.` };
|
|
282
291
|
}
|
|
283
|
-
return { found: true, ...resolved.item };
|
|
284
292
|
}
|
|
285
293
|
case "decantr_suggest_patterns": {
|
|
286
294
|
const err = validateStringArg(args, "description");
|
|
287
295
|
if (err) return { error: err };
|
|
288
296
|
const desc = args.description.toLowerCase();
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
p.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (
|
|
317
|
-
if (
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (score > 0) {
|
|
333
|
-
const preset = p.presets && typeof p.presets === "object" ? Object.values(p.presets)[0] : null;
|
|
334
|
-
suggestions.push({
|
|
335
|
-
id,
|
|
336
|
-
score,
|
|
337
|
-
name: p.name || id,
|
|
338
|
-
description: p.description || "",
|
|
339
|
-
components: p.components || [],
|
|
340
|
-
layout: preset?.layout ? preset.layout.layout || "grid" : "grid"
|
|
341
|
-
});
|
|
297
|
+
try {
|
|
298
|
+
const patternsResponse = await apiClient.listContent("patterns", {
|
|
299
|
+
namespace: "@official",
|
|
300
|
+
limit: 100
|
|
301
|
+
});
|
|
302
|
+
const suggestions = [];
|
|
303
|
+
for (const p of patternsResponse.items) {
|
|
304
|
+
const searchable = [
|
|
305
|
+
p.name || "",
|
|
306
|
+
p.description || "",
|
|
307
|
+
...p.components || [],
|
|
308
|
+
...p.tags || []
|
|
309
|
+
].join(" ").toLowerCase();
|
|
310
|
+
let score = 0;
|
|
311
|
+
const words = desc.split(/\s+/);
|
|
312
|
+
for (const word of words) {
|
|
313
|
+
if (word.length < 3) continue;
|
|
314
|
+
if (searchable.includes(word)) score += 10;
|
|
315
|
+
}
|
|
316
|
+
if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(p.id)) score += 20;
|
|
317
|
+
if (desc.includes("metric") && p.id === "kpi-grid") score += 15;
|
|
318
|
+
if (desc.includes("chart") && p.id === "chart-grid") score += 15;
|
|
319
|
+
if (desc.includes("table") && p.id === "data-table") score += 15;
|
|
320
|
+
if (desc.includes("form") && p.id === "form-sections") score += 15;
|
|
321
|
+
if (desc.includes("setting") && p.id === "form-sections") score += 15;
|
|
322
|
+
if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(p.id)) score += 20;
|
|
323
|
+
if (desc.includes("hero") && p.id === "hero") score += 20;
|
|
324
|
+
if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(p.id)) score += 15;
|
|
325
|
+
if (desc.includes("product") && p.id === "card-grid") score += 15;
|
|
326
|
+
if (desc.includes("feed") && p.id === "activity-feed") score += 15;
|
|
327
|
+
if (desc.includes("filter") && p.id === "filter-bar") score += 15;
|
|
328
|
+
if (desc.includes("search") && p.id === "filter-bar") score += 10;
|
|
329
|
+
if (score > 0) {
|
|
330
|
+
const preset = p.presets ? Object.values(p.presets)[0] : null;
|
|
331
|
+
suggestions.push({
|
|
332
|
+
id: p.id,
|
|
333
|
+
score,
|
|
334
|
+
name: p.name || p.id,
|
|
335
|
+
description: p.description || "",
|
|
336
|
+
components: p.components || [],
|
|
337
|
+
layout: preset?.layout ? preset.layout.layout : "grid"
|
|
338
|
+
});
|
|
339
|
+
}
|
|
342
340
|
}
|
|
341
|
+
suggestions.sort((a, b) => b.score - a.score);
|
|
342
|
+
return {
|
|
343
|
+
query: args.description,
|
|
344
|
+
suggestions: suggestions.slice(0, 5),
|
|
345
|
+
total: suggestions.length
|
|
346
|
+
};
|
|
347
|
+
} catch (e) {
|
|
348
|
+
return { error: `Could not fetch patterns: ${e.message}` };
|
|
343
349
|
}
|
|
344
|
-
suggestions.sort((a, b) => b.score - a.score);
|
|
345
|
-
return {
|
|
346
|
-
query: args.description,
|
|
347
|
-
suggestions: suggestions.slice(0, 5),
|
|
348
|
-
total: suggestions.length
|
|
349
|
-
};
|
|
350
350
|
}
|
|
351
351
|
case "decantr_check_drift": {
|
|
352
|
-
const essencePath = args.path ||
|
|
352
|
+
const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
|
|
353
353
|
let essence;
|
|
354
354
|
try {
|
|
355
355
|
essence = JSON.parse(await readFile(essencePath, "utf-8"));
|
|
@@ -417,7 +417,6 @@ async function handleTool(name, args) {
|
|
|
417
417
|
"ecommerce-admin",
|
|
418
418
|
"workbench"
|
|
419
419
|
];
|
|
420
|
-
const resolver = getResolver();
|
|
421
420
|
for (const id of archetypeIds) {
|
|
422
421
|
let score = 0;
|
|
423
422
|
if (desc.includes("dashboard") && id.includes("dashboard")) score += 20;
|
|
@@ -437,9 +436,14 @@ async function handleTool(name, args) {
|
|
|
437
436
|
}
|
|
438
437
|
archetypeScores.sort((a, b) => b.score - a.score);
|
|
439
438
|
const bestMatch = archetypeScores[0]?.id || "saas-dashboard";
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
let pages;
|
|
440
|
+
let features = [];
|
|
441
|
+
try {
|
|
442
|
+
const archetype = await apiClient.getArchetype("@official", bestMatch);
|
|
443
|
+
pages = archetype.pages;
|
|
444
|
+
features = archetype.features || [];
|
|
445
|
+
} catch {
|
|
446
|
+
}
|
|
443
447
|
const structure = (pages || [{ id: "home", shell: "full-bleed", default_layout: ["hero"] }]).map((p) => ({
|
|
444
448
|
id: p.id,
|
|
445
449
|
shell: p.shell || "sidebar-main",
|
|
@@ -457,7 +461,7 @@ async function handleTool(name, args) {
|
|
|
457
461
|
personality: ["professional"],
|
|
458
462
|
platform: { type: "spa", routing: "hash" },
|
|
459
463
|
structure,
|
|
460
|
-
features
|
|
464
|
+
features,
|
|
461
465
|
guard: { enforce_style: true, enforce_recipe: true, mode: "strict" },
|
|
462
466
|
density: { level: "comfortable", content_gap: "_gap4" },
|
|
463
467
|
target: framework,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/mcp-server",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "MCP server for Decantr — exposes design intelligence tools to AI coding assistants",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -15,20 +15,18 @@
|
|
|
15
15
|
},
|
|
16
16
|
"main": "dist/index.js",
|
|
17
17
|
"types": "dist/index.d.ts",
|
|
18
|
-
"files": [
|
|
19
|
-
"dist"
|
|
20
|
-
],
|
|
18
|
+
"files": ["dist"],
|
|
21
19
|
"publishConfig": {
|
|
22
20
|
"access": "public"
|
|
23
21
|
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
26
|
-
"@decantr/essence-spec": "1.0.0-beta.3",
|
|
27
|
-
"@decantr/registry": "1.0.0-beta.3"
|
|
28
|
-
},
|
|
29
22
|
"scripts": {
|
|
30
23
|
"build": "tsup",
|
|
31
24
|
"test": "vitest run",
|
|
32
25
|
"test:watch": "vitest"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
29
|
+
"@decantr/essence-spec": "workspace:*",
|
|
30
|
+
"@decantr/registry": "workspace:*"
|
|
33
31
|
}
|
|
34
|
-
}
|
|
32
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Decantr AI
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|