@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.
Files changed (3) hide show
  1. package/dist/index.js +120 -116
  2. package/package.json +8 -10
  3. 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 as join2 } from "path";
13
+ import { join } from "path";
14
14
  import { validateEssence, evaluateGuard } from "@decantr/essence-spec";
15
- import { createRegistryClient, resolvePatternPreset } from "@decantr/registry";
15
+ import { resolvePatternPreset } from "@decantr/registry";
16
16
 
17
17
  // src/helpers.ts
18
- import { createResolver } from "@decantr/registry";
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 _resolver = null;
32
- function getResolver() {
33
- if (!_resolver) {
34
- const envRoot = process.env.DECANTR_CONTENT_ROOT;
35
- const bundledRoot = join(import.meta.dirname, "..", "..", "..", "content");
36
- const npmRoot = join(process.cwd(), "node_modules", "@decantr", "content");
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 _resolver;
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 || join2(process.cwd(), "decantr.essence.json");
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 || join2(process.cwd(), "decantr.essence.json");
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 client = createRegistryClient();
225
- const results = await client.search(args.query, args.type);
225
+ const response = await apiClient.search({
226
+ q: args.query,
227
+ type: args.type
228
+ });
226
229
  return {
227
- total: results.length,
228
- results: results.map((r) => ({
230
+ total: response.total,
231
+ results: response.results.map((r) => ({
229
232
  type: r.type,
230
- id: r.id,
233
+ id: r.slug,
234
+ namespace: r.namespace,
231
235
  name: r.name,
232
236
  description: r.description,
233
- install: `decantr registry add ${r.type}/${r.id}`
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 resolver = getResolver();
244
- const resolved = await resolver.resolve("pattern", args.id);
245
- if (!resolved) {
246
- return { found: false, message: `Pattern "${args.id}" not found.` };
247
- }
248
- const result = { found: true, ...resolved.item };
249
- if (args.preset && typeof args.preset === "string") {
250
- const preset = resolvePatternPreset(resolved.item, args.preset);
251
- if (preset) result.resolvedPreset = preset;
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 resolver = getResolver();
259
- const resolved = await resolver.resolve("archetype", args.id);
260
- if (!resolved) {
261
- return { found: false, message: `Archetype "${args.id}" not 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 resolver = getResolver();
269
- const resolved = await resolver.resolve("recipe", args.id);
270
- if (!resolved) {
271
- return { found: false, message: `Recipe "${args.id}" not 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 resolver = getResolver();
279
- const resolved = await resolver.resolve("blueprint", args.id);
280
- if (!resolved) {
281
- return { found: false, message: `Blueprint "${args.id}" not 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
- const resolver = getResolver();
290
- const patternIds = [
291
- "hero",
292
- "kpi-grid",
293
- "data-table",
294
- "card-grid",
295
- "chart-grid",
296
- "filter-bar",
297
- "form-sections",
298
- "detail-header",
299
- "activity-feed",
300
- "cta-section"
301
- ];
302
- const suggestions = [];
303
- for (const id of patternIds) {
304
- const resolved = await resolver.resolve("pattern", id);
305
- if (!resolved) continue;
306
- const p = resolved.item;
307
- const searchable = [
308
- p.name || "",
309
- p.description || "",
310
- ...p.components || [],
311
- ...p.tags || []
312
- ].join(" ").toLowerCase();
313
- let score = 0;
314
- const words = desc.split(/\s+/);
315
- for (const word of words) {
316
- if (word.length < 3) continue;
317
- if (searchable.includes(word)) score += 10;
318
- }
319
- if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(id)) score += 20;
320
- if (desc.includes("metric") && id === "kpi-grid") score += 15;
321
- if (desc.includes("chart") && id === "chart-grid") score += 15;
322
- if (desc.includes("table") && id === "data-table") score += 15;
323
- if (desc.includes("form") && id === "form-sections") score += 15;
324
- if (desc.includes("setting") && id === "form-sections") score += 15;
325
- if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(id)) score += 20;
326
- if (desc.includes("hero") && id === "hero") score += 20;
327
- if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(id)) score += 15;
328
- if (desc.includes("product") && id === "card-grid") score += 15;
329
- if (desc.includes("feed") && id === "activity-feed") score += 15;
330
- if (desc.includes("filter") && id === "filter-bar") score += 15;
331
- if (desc.includes("search") && id === "filter-bar") score += 10;
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 || join2(process.cwd(), "decantr.essence.json");
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
- const archetypeResult = await resolver.resolve("archetype", bestMatch);
441
- const archetype = archetypeResult?.item;
442
- const pages = archetype?.pages;
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: archetype?.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",
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.