@decantr/mcp-server 1.0.0-beta.4 → 1.0.0-beta.5

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/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/bin.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "./chunk-QUSQLU5X.js";
@@ -0,0 +1,512 @@
1
+ // src/index.ts
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import {
5
+ ListToolsRequestSchema,
6
+ CallToolRequestSchema
7
+ } from "@modelcontextprotocol/sdk/types.js";
8
+
9
+ // src/tools.ts
10
+ import { readFile } from "fs/promises";
11
+ import { join } from "path";
12
+ import { validateEssence, evaluateGuard } from "@decantr/essence-spec";
13
+ import { resolvePatternPreset } from "@decantr/registry";
14
+
15
+ // src/helpers.ts
16
+ import { RegistryAPIClient } from "@decantr/registry";
17
+ var MAX_INPUT_LENGTH = 1e3;
18
+ function validateStringArg(args, field) {
19
+ const val = args[field];
20
+ if (!val || typeof val !== "string") {
21
+ return `Required parameter "${field}" must be a non-empty string.`;
22
+ }
23
+ if (val.length > MAX_INPUT_LENGTH) {
24
+ return `Parameter "${field}" exceeds maximum length of ${MAX_INPUT_LENGTH} characters.`;
25
+ }
26
+ return null;
27
+ }
28
+ var _apiClient = null;
29
+ function getAPIClient() {
30
+ if (!_apiClient) {
31
+ _apiClient = new RegistryAPIClient({
32
+ baseUrl: process.env.DECANTR_API_URL || void 0,
33
+ apiKey: process.env.DECANTR_API_KEY || void 0
34
+ });
35
+ }
36
+ return _apiClient;
37
+ }
38
+
39
+ // src/tools.ts
40
+ var READ_ONLY = {
41
+ readOnlyHint: true,
42
+ destructiveHint: false,
43
+ idempotentHint: true,
44
+ openWorldHint: false
45
+ };
46
+ var TOOLS = [
47
+ {
48
+ name: "decantr_read_essence",
49
+ title: "Read Essence",
50
+ description: "Read and return the current decantr.essence.json file from the working directory.",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: {
54
+ path: { type: "string", description: "Optional path to essence file. Defaults to ./decantr.essence.json." }
55
+ }
56
+ },
57
+ annotations: READ_ONLY
58
+ },
59
+ {
60
+ name: "decantr_validate",
61
+ title: "Validate Essence",
62
+ description: "Validate a decantr.essence.json file against the schema and guard rules. Returns errors and warnings.",
63
+ inputSchema: {
64
+ type: "object",
65
+ properties: {
66
+ path: { type: "string", description: "Path to essence file. Defaults to ./decantr.essence.json." }
67
+ }
68
+ },
69
+ annotations: READ_ONLY
70
+ },
71
+ {
72
+ name: "decantr_search_registry",
73
+ title: "Search Registry",
74
+ description: "Search the Decantr community content registry for patterns, archetypes, recipes, and styles.",
75
+ inputSchema: {
76
+ type: "object",
77
+ properties: {
78
+ query: { type: "string", description: 'Search query (e.g. "kanban", "neon", "dashboard")' },
79
+ type: { type: "string", description: "Filter by type: pattern, archetype, recipe, style" }
80
+ },
81
+ required: ["query"]
82
+ },
83
+ annotations: READ_ONLY
84
+ },
85
+ {
86
+ name: "decantr_resolve_pattern",
87
+ title: "Resolve Pattern",
88
+ description: "Get full pattern details including layout spec, components, presets, and code examples.",
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ id: { type: "string", description: 'Pattern ID (e.g. "hero", "data-table", "kpi-grid")' },
93
+ preset: { type: "string", description: 'Optional preset name (e.g. "product", "content")' },
94
+ namespace: { type: "string", description: 'Namespace (default: "@official")' }
95
+ },
96
+ required: ["id"]
97
+ },
98
+ annotations: READ_ONLY
99
+ },
100
+ {
101
+ name: "decantr_resolve_archetype",
102
+ title: "Resolve Archetype",
103
+ description: "Get archetype details including default pages, layouts, features, and suggested theme.",
104
+ inputSchema: {
105
+ type: "object",
106
+ properties: {
107
+ id: { type: "string", description: 'Archetype ID (e.g. "saas-dashboard", "ecommerce")' },
108
+ namespace: { type: "string", description: 'Namespace (default: "@official")' }
109
+ },
110
+ required: ["id"]
111
+ },
112
+ annotations: READ_ONLY
113
+ },
114
+ {
115
+ name: "decantr_resolve_recipe",
116
+ title: "Resolve Recipe",
117
+ description: "Get recipe decoration rules including shell styles, spatial hints, visual effects, and pattern preferences.",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ id: { type: "string", description: 'Recipe ID (e.g. "auradecantism")' },
122
+ namespace: { type: "string", description: 'Namespace (default: "@official")' }
123
+ },
124
+ required: ["id"]
125
+ },
126
+ annotations: READ_ONLY
127
+ },
128
+ {
129
+ name: "decantr_resolve_blueprint",
130
+ title: "Resolve Blueprint",
131
+ description: "Get a blueprint (app composition) with its archetype list, suggested theme, personality traits, and full page structure.",
132
+ inputSchema: {
133
+ type: "object",
134
+ properties: {
135
+ id: { type: "string", description: 'Blueprint ID (e.g. "saas-dashboard", "ecommerce", "portfolio")' },
136
+ namespace: { type: "string", description: 'Namespace (default: "@official")' }
137
+ },
138
+ required: ["id"]
139
+ },
140
+ annotations: READ_ONLY
141
+ },
142
+ {
143
+ name: "decantr_suggest_patterns",
144
+ title: "Suggest Patterns",
145
+ description: "Given a page description, suggest appropriate patterns from the registry. Returns ranked pattern matches with layout specs and component lists.",
146
+ inputSchema: {
147
+ type: "object",
148
+ properties: {
149
+ description: { type: "string", description: 'Description of the page or section (e.g. "dashboard with metrics and charts", "settings form with toggles")' }
150
+ },
151
+ required: ["description"]
152
+ },
153
+ annotations: READ_ONLY
154
+ },
155
+ {
156
+ name: "decantr_check_drift",
157
+ title: "Check Drift",
158
+ description: "Check if code changes violate the design intent captured in the Essence spec. Returns guard rule violations with severity and fix suggestions.",
159
+ inputSchema: {
160
+ type: "object",
161
+ properties: {
162
+ path: { type: "string", description: "Path to essence file. Defaults to ./decantr.essence.json." },
163
+ page_id: { type: "string", description: 'Page ID being modified (e.g. "overview", "settings")' },
164
+ components_used: {
165
+ type: "array",
166
+ items: { type: "string" },
167
+ description: "List of component names used in the generated code"
168
+ },
169
+ theme_used: { type: "string", description: "Theme/style name used in the generated code" }
170
+ }
171
+ },
172
+ annotations: READ_ONLY
173
+ },
174
+ {
175
+ name: "decantr_create_essence",
176
+ title: "Create Essence",
177
+ description: "Generate a valid Essence spec skeleton from a project description. Returns a structured essence.json template based on the closest matching archetype and blueprint.",
178
+ inputSchema: {
179
+ type: "object",
180
+ properties: {
181
+ description: { type: "string", description: 'Natural language project description (e.g. "SaaS dashboard with analytics, user management, and billing")' },
182
+ framework: { type: "string", description: 'Target framework (e.g. "react", "vue", "svelte"). Defaults to "react".' }
183
+ },
184
+ required: ["description"]
185
+ },
186
+ annotations: READ_ONLY
187
+ }
188
+ ];
189
+ async function handleTool(name, args) {
190
+ const apiClient = getAPIClient();
191
+ switch (name) {
192
+ case "decantr_read_essence": {
193
+ const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
194
+ try {
195
+ const raw = await readFile(essencePath, "utf-8");
196
+ return JSON.parse(raw);
197
+ } catch (e) {
198
+ return { error: `Could not read essence file: ${e.message}` };
199
+ }
200
+ }
201
+ case "decantr_validate": {
202
+ const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
203
+ let essence;
204
+ try {
205
+ essence = JSON.parse(await readFile(essencePath, "utf-8"));
206
+ } catch (e) {
207
+ return { valid: false, errors: [`Could not read: ${e.message}`], guardViolations: [] };
208
+ }
209
+ const result = validateEssence(essence);
210
+ let guardViolations = [];
211
+ if (result.valid && typeof essence === "object" && essence !== null) {
212
+ try {
213
+ guardViolations = evaluateGuard(essence, {});
214
+ } catch {
215
+ }
216
+ }
217
+ return { ...result, guardViolations };
218
+ }
219
+ case "decantr_search_registry": {
220
+ const err = validateStringArg(args, "query");
221
+ if (err) return { error: err };
222
+ try {
223
+ const response = await apiClient.search({
224
+ q: args.query,
225
+ type: args.type
226
+ });
227
+ return {
228
+ total: response.total,
229
+ results: response.results.map((r) => ({
230
+ type: r.type,
231
+ id: r.slug,
232
+ namespace: r.namespace,
233
+ name: r.name,
234
+ description: r.description,
235
+ install: `decantr get ${r.type} ${r.slug}`
236
+ }))
237
+ };
238
+ } catch (e) {
239
+ return { error: `Search failed: ${e.message}` };
240
+ }
241
+ }
242
+ case "decantr_resolve_pattern": {
243
+ const err = validateStringArg(args, "id");
244
+ if (err) return { error: err };
245
+ const namespace = args.namespace || "@official";
246
+ try {
247
+ const pattern = await apiClient.getPattern(namespace, args.id);
248
+ const result = { found: true, ...pattern };
249
+ if (args.preset && typeof args.preset === "string") {
250
+ const preset = resolvePatternPreset(pattern, args.preset);
251
+ if (preset) result.resolvedPreset = preset;
252
+ }
253
+ return result;
254
+ } catch {
255
+ return { found: false, message: `Pattern "${args.id}" not found in ${namespace}.` };
256
+ }
257
+ }
258
+ case "decantr_resolve_archetype": {
259
+ const err = validateStringArg(args, "id");
260
+ if (err) return { error: err };
261
+ const namespace = args.namespace || "@official";
262
+ try {
263
+ const archetype = await apiClient.getArchetype(namespace, args.id);
264
+ return { found: true, ...archetype };
265
+ } catch {
266
+ return { found: false, message: `Archetype "${args.id}" not found in ${namespace}.` };
267
+ }
268
+ }
269
+ case "decantr_resolve_recipe": {
270
+ const err = validateStringArg(args, "id");
271
+ if (err) return { error: err };
272
+ const namespace = args.namespace || "@official";
273
+ try {
274
+ const recipe = await apiClient.getRecipe(namespace, args.id);
275
+ return { found: true, ...recipe };
276
+ } catch {
277
+ return { found: false, message: `Recipe "${args.id}" not found in ${namespace}.` };
278
+ }
279
+ }
280
+ case "decantr_resolve_blueprint": {
281
+ const err = validateStringArg(args, "id");
282
+ if (err) return { error: err };
283
+ const namespace = args.namespace || "@official";
284
+ try {
285
+ const blueprint = await apiClient.getBlueprint(namespace, args.id);
286
+ return { found: true, ...blueprint };
287
+ } catch {
288
+ return { found: false, message: `Blueprint "${args.id}" not found in ${namespace}.` };
289
+ }
290
+ }
291
+ case "decantr_suggest_patterns": {
292
+ const err = validateStringArg(args, "description");
293
+ if (err) return { error: err };
294
+ const desc = args.description.toLowerCase();
295
+ try {
296
+ const patternsResponse = await apiClient.listContent("patterns", {
297
+ namespace: "@official",
298
+ limit: 100
299
+ });
300
+ const suggestions = [];
301
+ for (const p of patternsResponse.items) {
302
+ const searchable = [
303
+ p.name || "",
304
+ p.description || "",
305
+ ...p.components || [],
306
+ ...p.tags || []
307
+ ].join(" ").toLowerCase();
308
+ let score = 0;
309
+ const words = desc.split(/\s+/);
310
+ for (const word of words) {
311
+ if (word.length < 3) continue;
312
+ if (searchable.includes(word)) score += 10;
313
+ }
314
+ if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(p.id)) score += 20;
315
+ if (desc.includes("metric") && p.id === "kpi-grid") score += 15;
316
+ if (desc.includes("chart") && p.id === "chart-grid") score += 15;
317
+ if (desc.includes("table") && p.id === "data-table") score += 15;
318
+ if (desc.includes("form") && p.id === "form-sections") score += 15;
319
+ if (desc.includes("setting") && p.id === "form-sections") score += 15;
320
+ if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(p.id)) score += 20;
321
+ if (desc.includes("hero") && p.id === "hero") score += 20;
322
+ if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(p.id)) score += 15;
323
+ if (desc.includes("product") && p.id === "card-grid") score += 15;
324
+ if (desc.includes("feed") && p.id === "activity-feed") score += 15;
325
+ if (desc.includes("filter") && p.id === "filter-bar") score += 15;
326
+ if (desc.includes("search") && p.id === "filter-bar") score += 10;
327
+ if (score > 0) {
328
+ const preset = p.presets ? Object.values(p.presets)[0] : null;
329
+ suggestions.push({
330
+ id: p.id,
331
+ score,
332
+ name: p.name || p.id,
333
+ description: p.description || "",
334
+ components: p.components || [],
335
+ layout: preset?.layout ? preset.layout.layout : "grid"
336
+ });
337
+ }
338
+ }
339
+ suggestions.sort((a, b) => b.score - a.score);
340
+ return {
341
+ query: args.description,
342
+ suggestions: suggestions.slice(0, 5),
343
+ total: suggestions.length
344
+ };
345
+ } catch (e) {
346
+ return { error: `Could not fetch patterns: ${e.message}` };
347
+ }
348
+ }
349
+ case "decantr_check_drift": {
350
+ const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
351
+ let essence;
352
+ try {
353
+ essence = JSON.parse(await readFile(essencePath, "utf-8"));
354
+ } catch (e) {
355
+ return { error: `Could not read essence: ${e.message}` };
356
+ }
357
+ const validation = validateEssence(essence);
358
+ if (!validation.valid) {
359
+ return { drifted: true, reason: "invalid_essence", errors: validation.errors };
360
+ }
361
+ const violations = [];
362
+ if (args.theme_used && typeof args.theme_used === "string") {
363
+ const expectedTheme = essence.theme;
364
+ if (expectedTheme?.style && args.theme_used !== expectedTheme.style) {
365
+ violations.push({
366
+ rule: "theme-match",
367
+ severity: "critical",
368
+ message: `Theme drift: code uses "${args.theme_used}" but Essence specifies "${expectedTheme.style}". Do not switch themes.`
369
+ });
370
+ }
371
+ }
372
+ if (args.page_id && typeof args.page_id === "string") {
373
+ const structure = essence.structure;
374
+ if (structure && !structure.find((p) => p.id === args.page_id)) {
375
+ violations.push({
376
+ rule: "page-exists",
377
+ severity: "critical",
378
+ message: `Page "${args.page_id}" not found in Essence structure. Add it to the Essence before generating code for it.`
379
+ });
380
+ }
381
+ }
382
+ try {
383
+ const guardViolations = evaluateGuard(essence, {
384
+ pageId: args.page_id
385
+ });
386
+ for (const gv of guardViolations) {
387
+ violations.push({
388
+ rule: gv.rule || "guard",
389
+ severity: gv.severity || "warning",
390
+ message: gv.message || "Guard violation"
391
+ });
392
+ }
393
+ } catch {
394
+ }
395
+ return {
396
+ drifted: violations.length > 0,
397
+ violations,
398
+ checkedAgainst: essencePath
399
+ };
400
+ }
401
+ case "decantr_create_essence": {
402
+ const err = validateStringArg(args, "description");
403
+ if (err) return { error: err };
404
+ const desc = args.description.toLowerCase();
405
+ const framework = args.framework || "react";
406
+ const archetypeScores = [];
407
+ const archetypeIds = [
408
+ "saas-dashboard",
409
+ "ecommerce",
410
+ "portfolio",
411
+ "content-site",
412
+ "financial-dashboard",
413
+ "cloud-platform",
414
+ "gaming-platform",
415
+ "ecommerce-admin",
416
+ "workbench"
417
+ ];
418
+ for (const id of archetypeIds) {
419
+ let score = 0;
420
+ if (desc.includes("dashboard") && id.includes("dashboard")) score += 20;
421
+ if (desc.includes("saas") && id.includes("saas")) score += 20;
422
+ if (desc.includes("ecommerce") && id.includes("ecommerce")) score += 20;
423
+ if (desc.includes("shop") && id.includes("ecommerce")) score += 15;
424
+ if (desc.includes("portfolio") && id.includes("portfolio")) score += 20;
425
+ if (desc.includes("blog") && id.includes("content")) score += 15;
426
+ if (desc.includes("content") && id.includes("content")) score += 15;
427
+ if (desc.includes("finance") && id.includes("financial")) score += 20;
428
+ if (desc.includes("cloud") && id.includes("cloud")) score += 15;
429
+ if (desc.includes("game") && id.includes("gaming")) score += 15;
430
+ if (desc.includes("admin") && id.includes("admin")) score += 15;
431
+ if (desc.includes("analytics") && id.includes("dashboard")) score += 10;
432
+ if (desc.includes("tool") && id === "workbench") score += 10;
433
+ if (score > 0) archetypeScores.push({ id, score });
434
+ }
435
+ archetypeScores.sort((a, b) => b.score - a.score);
436
+ const bestMatch = archetypeScores[0]?.id || "saas-dashboard";
437
+ let pages;
438
+ let features = [];
439
+ try {
440
+ const archetype = await apiClient.getArchetype("@official", bestMatch);
441
+ pages = archetype.pages;
442
+ features = archetype.features || [];
443
+ } catch {
444
+ }
445
+ const structure = (pages || [{ id: "home", shell: "full-bleed", default_layout: ["hero"] }]).map((p) => ({
446
+ id: p.id,
447
+ shell: p.shell || "sidebar-main",
448
+ layout: p.default_layout || []
449
+ }));
450
+ const essence = {
451
+ version: "2.0.0",
452
+ archetype: bestMatch,
453
+ theme: {
454
+ style: "auradecantism",
455
+ mode: "dark",
456
+ recipe: "auradecantism",
457
+ shape: "rounded"
458
+ },
459
+ personality: ["professional"],
460
+ platform: { type: "spa", routing: "hash" },
461
+ structure,
462
+ features,
463
+ guard: { enforce_style: true, enforce_recipe: true, mode: "strict" },
464
+ density: { level: "comfortable", content_gap: "_gap4" },
465
+ target: framework,
466
+ _generated: {
467
+ matched_archetype: bestMatch,
468
+ confidence: archetypeScores[0]?.score || 0,
469
+ alternatives: archetypeScores.slice(1, 4).map((a) => a.id),
470
+ description: args.description
471
+ }
472
+ };
473
+ return {
474
+ essence,
475
+ archetype: bestMatch,
476
+ instructions: `Save this as decantr.essence.json in your project root. Review the structure (pages, patterns) and adjust to match your needs. The guard rules will validate your code against this spec.`
477
+ };
478
+ }
479
+ default:
480
+ return { error: `Unknown tool: ${name}` };
481
+ }
482
+ }
483
+
484
+ // src/index.ts
485
+ var VERSION = "0.1.0";
486
+ var server = new Server(
487
+ { name: "decantr", version: VERSION },
488
+ { capabilities: { tools: {} } }
489
+ );
490
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
491
+ return { tools: TOOLS };
492
+ });
493
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
494
+ const { name, arguments: args } = request.params;
495
+ try {
496
+ const result = await handleTool(name, args ?? {});
497
+ const response = {
498
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
499
+ };
500
+ if (result && typeof result === "object" && "error" in result) {
501
+ response.isError = true;
502
+ }
503
+ return response;
504
+ } catch (err) {
505
+ return {
506
+ content: [{ type: "text", text: JSON.stringify({ error: err.message }) }],
507
+ isError: true
508
+ };
509
+ }
510
+ });
511
+ var transport = new StdioServerTransport();
512
+ await server.connect(transport);
package/dist/index.js CHANGED
@@ -1,514 +1 @@
1
- #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import {
7
- ListToolsRequestSchema,
8
- CallToolRequestSchema
9
- } from "@modelcontextprotocol/sdk/types.js";
10
-
11
- // src/tools.ts
12
- import { readFile } from "fs/promises";
13
- import { join } from "path";
14
- import { validateEssence, evaluateGuard } from "@decantr/essence-spec";
15
- import { resolvePatternPreset } from "@decantr/registry";
16
-
17
- // src/helpers.ts
18
- import { RegistryAPIClient } from "@decantr/registry";
19
- var MAX_INPUT_LENGTH = 1e3;
20
- function validateStringArg(args, field) {
21
- const val = args[field];
22
- if (!val || typeof val !== "string") {
23
- return `Required parameter "${field}" must be a non-empty string.`;
24
- }
25
- if (val.length > MAX_INPUT_LENGTH) {
26
- return `Parameter "${field}" exceeds maximum length of ${MAX_INPUT_LENGTH} characters.`;
27
- }
28
- return null;
29
- }
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
36
- });
37
- }
38
- return _apiClient;
39
- }
40
-
41
- // src/tools.ts
42
- var READ_ONLY = {
43
- readOnlyHint: true,
44
- destructiveHint: false,
45
- idempotentHint: true,
46
- openWorldHint: false
47
- };
48
- var TOOLS = [
49
- {
50
- name: "decantr_read_essence",
51
- title: "Read Essence",
52
- description: "Read and return the current decantr.essence.json file from the working directory.",
53
- inputSchema: {
54
- type: "object",
55
- properties: {
56
- path: { type: "string", description: "Optional path to essence file. Defaults to ./decantr.essence.json." }
57
- }
58
- },
59
- annotations: READ_ONLY
60
- },
61
- {
62
- name: "decantr_validate",
63
- title: "Validate Essence",
64
- description: "Validate a decantr.essence.json file against the schema and guard rules. Returns errors and warnings.",
65
- inputSchema: {
66
- type: "object",
67
- properties: {
68
- path: { type: "string", description: "Path to essence file. Defaults to ./decantr.essence.json." }
69
- }
70
- },
71
- annotations: READ_ONLY
72
- },
73
- {
74
- name: "decantr_search_registry",
75
- title: "Search Registry",
76
- description: "Search the Decantr community content registry for patterns, archetypes, recipes, and styles.",
77
- inputSchema: {
78
- type: "object",
79
- properties: {
80
- query: { type: "string", description: 'Search query (e.g. "kanban", "neon", "dashboard")' },
81
- type: { type: "string", description: "Filter by type: pattern, archetype, recipe, style" }
82
- },
83
- required: ["query"]
84
- },
85
- annotations: READ_ONLY
86
- },
87
- {
88
- name: "decantr_resolve_pattern",
89
- title: "Resolve Pattern",
90
- description: "Get full pattern details including layout spec, components, presets, and code examples.",
91
- inputSchema: {
92
- type: "object",
93
- properties: {
94
- id: { type: "string", description: 'Pattern ID (e.g. "hero", "data-table", "kpi-grid")' },
95
- preset: { type: "string", description: 'Optional preset name (e.g. "product", "content")' },
96
- namespace: { type: "string", description: 'Namespace (default: "@official")' }
97
- },
98
- required: ["id"]
99
- },
100
- annotations: READ_ONLY
101
- },
102
- {
103
- name: "decantr_resolve_archetype",
104
- title: "Resolve Archetype",
105
- description: "Get archetype details including default pages, layouts, features, and suggested theme.",
106
- inputSchema: {
107
- type: "object",
108
- properties: {
109
- id: { type: "string", description: 'Archetype ID (e.g. "saas-dashboard", "ecommerce")' },
110
- namespace: { type: "string", description: 'Namespace (default: "@official")' }
111
- },
112
- required: ["id"]
113
- },
114
- annotations: READ_ONLY
115
- },
116
- {
117
- name: "decantr_resolve_recipe",
118
- title: "Resolve Recipe",
119
- description: "Get recipe decoration rules including shell styles, spatial hints, visual effects, and pattern preferences.",
120
- inputSchema: {
121
- type: "object",
122
- properties: {
123
- id: { type: "string", description: 'Recipe ID (e.g. "auradecantism")' },
124
- namespace: { type: "string", description: 'Namespace (default: "@official")' }
125
- },
126
- required: ["id"]
127
- },
128
- annotations: READ_ONLY
129
- },
130
- {
131
- name: "decantr_resolve_blueprint",
132
- title: "Resolve Blueprint",
133
- description: "Get a blueprint (app composition) with its archetype list, suggested theme, personality traits, and full page structure.",
134
- inputSchema: {
135
- type: "object",
136
- properties: {
137
- id: { type: "string", description: 'Blueprint ID (e.g. "saas-dashboard", "ecommerce", "portfolio")' },
138
- namespace: { type: "string", description: 'Namespace (default: "@official")' }
139
- },
140
- required: ["id"]
141
- },
142
- annotations: READ_ONLY
143
- },
144
- {
145
- name: "decantr_suggest_patterns",
146
- title: "Suggest Patterns",
147
- description: "Given a page description, suggest appropriate patterns from the registry. Returns ranked pattern matches with layout specs and component lists.",
148
- inputSchema: {
149
- type: "object",
150
- properties: {
151
- description: { type: "string", description: 'Description of the page or section (e.g. "dashboard with metrics and charts", "settings form with toggles")' }
152
- },
153
- required: ["description"]
154
- },
155
- annotations: READ_ONLY
156
- },
157
- {
158
- name: "decantr_check_drift",
159
- title: "Check Drift",
160
- description: "Check if code changes violate the design intent captured in the Essence spec. Returns guard rule violations with severity and fix suggestions.",
161
- inputSchema: {
162
- type: "object",
163
- properties: {
164
- path: { type: "string", description: "Path to essence file. Defaults to ./decantr.essence.json." },
165
- page_id: { type: "string", description: 'Page ID being modified (e.g. "overview", "settings")' },
166
- components_used: {
167
- type: "array",
168
- items: { type: "string" },
169
- description: "List of component names used in the generated code"
170
- },
171
- theme_used: { type: "string", description: "Theme/style name used in the generated code" }
172
- }
173
- },
174
- annotations: READ_ONLY
175
- },
176
- {
177
- name: "decantr_create_essence",
178
- title: "Create Essence",
179
- description: "Generate a valid Essence spec skeleton from a project description. Returns a structured essence.json template based on the closest matching archetype and blueprint.",
180
- inputSchema: {
181
- type: "object",
182
- properties: {
183
- description: { type: "string", description: 'Natural language project description (e.g. "SaaS dashboard with analytics, user management, and billing")' },
184
- framework: { type: "string", description: 'Target framework (e.g. "react", "vue", "svelte"). Defaults to "react".' }
185
- },
186
- required: ["description"]
187
- },
188
- annotations: READ_ONLY
189
- }
190
- ];
191
- async function handleTool(name, args) {
192
- const apiClient = getAPIClient();
193
- switch (name) {
194
- case "decantr_read_essence": {
195
- const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
196
- try {
197
- const raw = await readFile(essencePath, "utf-8");
198
- return JSON.parse(raw);
199
- } catch (e) {
200
- return { error: `Could not read essence file: ${e.message}` };
201
- }
202
- }
203
- case "decantr_validate": {
204
- const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
205
- let essence;
206
- try {
207
- essence = JSON.parse(await readFile(essencePath, "utf-8"));
208
- } catch (e) {
209
- return { valid: false, errors: [`Could not read: ${e.message}`], guardViolations: [] };
210
- }
211
- const result = validateEssence(essence);
212
- let guardViolations = [];
213
- if (result.valid && typeof essence === "object" && essence !== null) {
214
- try {
215
- guardViolations = evaluateGuard(essence, {});
216
- } catch {
217
- }
218
- }
219
- return { ...result, guardViolations };
220
- }
221
- case "decantr_search_registry": {
222
- const err = validateStringArg(args, "query");
223
- if (err) return { error: err };
224
- try {
225
- const response = await apiClient.search({
226
- q: args.query,
227
- type: args.type
228
- });
229
- return {
230
- total: response.total,
231
- results: response.results.map((r) => ({
232
- type: r.type,
233
- id: r.slug,
234
- namespace: r.namespace,
235
- name: r.name,
236
- description: r.description,
237
- install: `decantr get ${r.type} ${r.slug}`
238
- }))
239
- };
240
- } catch (e) {
241
- return { error: `Search failed: ${e.message}` };
242
- }
243
- }
244
- case "decantr_resolve_pattern": {
245
- const err = validateStringArg(args, "id");
246
- if (err) return { error: err };
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}.` };
258
- }
259
- }
260
- case "decantr_resolve_archetype": {
261
- const err = validateStringArg(args, "id");
262
- if (err) return { error: err };
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}.` };
269
- }
270
- }
271
- case "decantr_resolve_recipe": {
272
- const err = validateStringArg(args, "id");
273
- if (err) return { error: err };
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}.` };
280
- }
281
- }
282
- case "decantr_resolve_blueprint": {
283
- const err = validateStringArg(args, "id");
284
- if (err) return { error: err };
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}.` };
291
- }
292
- }
293
- case "decantr_suggest_patterns": {
294
- const err = validateStringArg(args, "description");
295
- if (err) return { error: err };
296
- const desc = args.description.toLowerCase();
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
- }
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}` };
349
- }
350
- }
351
- case "decantr_check_drift": {
352
- const essencePath = args.path || join(process.cwd(), "decantr.essence.json");
353
- let essence;
354
- try {
355
- essence = JSON.parse(await readFile(essencePath, "utf-8"));
356
- } catch (e) {
357
- return { error: `Could not read essence: ${e.message}` };
358
- }
359
- const validation = validateEssence(essence);
360
- if (!validation.valid) {
361
- return { drifted: true, reason: "invalid_essence", errors: validation.errors };
362
- }
363
- const violations = [];
364
- if (args.theme_used && typeof args.theme_used === "string") {
365
- const expectedTheme = essence.theme;
366
- if (expectedTheme?.style && args.theme_used !== expectedTheme.style) {
367
- violations.push({
368
- rule: "theme-match",
369
- severity: "critical",
370
- message: `Theme drift: code uses "${args.theme_used}" but Essence specifies "${expectedTheme.style}". Do not switch themes.`
371
- });
372
- }
373
- }
374
- if (args.page_id && typeof args.page_id === "string") {
375
- const structure = essence.structure;
376
- if (structure && !structure.find((p) => p.id === args.page_id)) {
377
- violations.push({
378
- rule: "page-exists",
379
- severity: "critical",
380
- message: `Page "${args.page_id}" not found in Essence structure. Add it to the Essence before generating code for it.`
381
- });
382
- }
383
- }
384
- try {
385
- const guardViolations = evaluateGuard(essence, {
386
- pageId: args.page_id
387
- });
388
- for (const gv of guardViolations) {
389
- violations.push({
390
- rule: gv.rule || "guard",
391
- severity: gv.severity || "warning",
392
- message: gv.message || "Guard violation"
393
- });
394
- }
395
- } catch {
396
- }
397
- return {
398
- drifted: violations.length > 0,
399
- violations,
400
- checkedAgainst: essencePath
401
- };
402
- }
403
- case "decantr_create_essence": {
404
- const err = validateStringArg(args, "description");
405
- if (err) return { error: err };
406
- const desc = args.description.toLowerCase();
407
- const framework = args.framework || "react";
408
- const archetypeScores = [];
409
- const archetypeIds = [
410
- "saas-dashboard",
411
- "ecommerce",
412
- "portfolio",
413
- "content-site",
414
- "financial-dashboard",
415
- "cloud-platform",
416
- "gaming-platform",
417
- "ecommerce-admin",
418
- "workbench"
419
- ];
420
- for (const id of archetypeIds) {
421
- let score = 0;
422
- if (desc.includes("dashboard") && id.includes("dashboard")) score += 20;
423
- if (desc.includes("saas") && id.includes("saas")) score += 20;
424
- if (desc.includes("ecommerce") && id.includes("ecommerce")) score += 20;
425
- if (desc.includes("shop") && id.includes("ecommerce")) score += 15;
426
- if (desc.includes("portfolio") && id.includes("portfolio")) score += 20;
427
- if (desc.includes("blog") && id.includes("content")) score += 15;
428
- if (desc.includes("content") && id.includes("content")) score += 15;
429
- if (desc.includes("finance") && id.includes("financial")) score += 20;
430
- if (desc.includes("cloud") && id.includes("cloud")) score += 15;
431
- if (desc.includes("game") && id.includes("gaming")) score += 15;
432
- if (desc.includes("admin") && id.includes("admin")) score += 15;
433
- if (desc.includes("analytics") && id.includes("dashboard")) score += 10;
434
- if (desc.includes("tool") && id === "workbench") score += 10;
435
- if (score > 0) archetypeScores.push({ id, score });
436
- }
437
- archetypeScores.sort((a, b) => b.score - a.score);
438
- const bestMatch = archetypeScores[0]?.id || "saas-dashboard";
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
- }
447
- const structure = (pages || [{ id: "home", shell: "full-bleed", default_layout: ["hero"] }]).map((p) => ({
448
- id: p.id,
449
- shell: p.shell || "sidebar-main",
450
- layout: p.default_layout || []
451
- }));
452
- const essence = {
453
- version: "2.0.0",
454
- archetype: bestMatch,
455
- theme: {
456
- style: "auradecantism",
457
- mode: "dark",
458
- recipe: "auradecantism",
459
- shape: "rounded"
460
- },
461
- personality: ["professional"],
462
- platform: { type: "spa", routing: "hash" },
463
- structure,
464
- features,
465
- guard: { enforce_style: true, enforce_recipe: true, mode: "strict" },
466
- density: { level: "comfortable", content_gap: "_gap4" },
467
- target: framework,
468
- _generated: {
469
- matched_archetype: bestMatch,
470
- confidence: archetypeScores[0]?.score || 0,
471
- alternatives: archetypeScores.slice(1, 4).map((a) => a.id),
472
- description: args.description
473
- }
474
- };
475
- return {
476
- essence,
477
- archetype: bestMatch,
478
- instructions: `Save this as decantr.essence.json in your project root. Review the structure (pages, patterns) and adjust to match your needs. The guard rules will validate your code against this spec.`
479
- };
480
- }
481
- default:
482
- return { error: `Unknown tool: ${name}` };
483
- }
484
- }
485
-
486
- // src/index.ts
487
- var VERSION = "0.1.0";
488
- var server = new Server(
489
- { name: "decantr", version: VERSION },
490
- { capabilities: { tools: {} } }
491
- );
492
- server.setRequestHandler(ListToolsRequestSchema, async () => {
493
- return { tools: TOOLS };
494
- });
495
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
496
- const { name, arguments: args } = request.params;
497
- try {
498
- const result = await handleTool(name, args ?? {});
499
- const response = {
500
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
501
- };
502
- if (result && typeof result === "object" && "error" in result) {
503
- response.isError = true;
504
- }
505
- return response;
506
- } catch (err) {
507
- return {
508
- content: [{ type: "text", text: JSON.stringify({ error: err.message }) }],
509
- isError: true
510
- };
511
- }
512
- });
513
- var transport = new StdioServerTransport();
514
- await server.connect(transport);
1
+ import "./chunk-QUSQLU5X.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/mcp-server",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "MCP server for Decantr — exposes design intelligence tools to AI coding assistants",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -11,7 +11,7 @@
11
11
  "homepage": "https://decantr.ai",
12
12
  "type": "module",
13
13
  "bin": {
14
- "decantr-mcp": "./dist/index.js"
14
+ "decantr-mcp": "./dist/bin.js"
15
15
  },
16
16
  "main": "dist/index.js",
17
17
  "types": "dist/index.d.ts",