@decantr/mcp-server 1.0.0-beta.10 → 1.0.0-beta.12

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/LICENSE ADDED
@@ -0,0 +1,21 @@
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.
package/README.md CHANGED
@@ -64,17 +64,16 @@ Add to your Windsurf MCP config (`~/.windsurf/mcp.json`):
64
64
  | `decantr_create_essence` | Generate an Essence spec skeleton from a project description | `{ "description": "SaaS dashboard with analytics and billing", "framework": "react" }` |
65
65
  | `decantr_read_essence` | Read the current `decantr.essence.json` from the working directory | `{}` or `{ "path": "./custom.essence.json" }` |
66
66
  | `decantr_validate` | Validate an Essence file against the schema and guard rules | `{ "path": "./decantr.essence.json" }` |
67
- | `decantr_search_registry` | Search the community registry for patterns, archetypes, recipes, and styles | `{ "query": "kanban", "type": "pattern" }` |
67
+ | `decantr_search_registry` | Search the community registry for patterns, archetypes, themes, and shells | `{ "query": "kanban", "type": "pattern" }` |
68
68
  | `decantr_resolve_pattern` | Get full pattern details: layout spec, components, presets, code examples | `{ "id": "data-table", "preset": "product" }` |
69
69
  | `decantr_resolve_archetype` | Get archetype details: default pages, layouts, features, suggested theme | `{ "id": "saas-dashboard" }` |
70
- | `decantr_resolve_recipe` | Get recipe decoration rules: shell styles, spatial hints, visual effects | `{ "id": "auradecantism" }` |
71
70
  | `decantr_resolve_blueprint` | Get a full app composition with page structure and personality traits | `{ "id": "ecommerce" }` |
72
71
  | `decantr_suggest_patterns` | Given a page description, get ranked pattern suggestions | `{ "description": "dashboard with metrics and charts" }` |
73
72
  | `decantr_check_drift` | Check if generated code violates the design intent in the Essence spec | `{ "page_id": "overview", "components_used": ["Card", "LineChart"], "theme_used": "auradecantism" }` |
74
73
 
75
74
  ## How It Works
76
75
 
77
- An Essence spec (`decantr.essence.json`) captures your design intent -- archetype, theme, page structure, patterns, and guard rules -- in a single declarative file. The MCP server exposes this spec and the Decantr registry to your AI assistant, giving it concrete layout specs, component lists, and decoration rules instead of relying on the model's generic training data. The result is generated code that follows a coherent design system, and drift detection that catches deviations before they ship.
76
+ An Essence spec (`decantr.essence.json`) captures your design intent -- archetype, theme, page structure, patterns, and guard rules -- in a single declarative file. The MCP server exposes this spec and the Decantr registry to your AI assistant, giving it concrete layout specs, component lists, and visual treatments instead of relying on the model's generic training data. The result is generated code that follows a coherent design system, and drift detection that catches deviations before they ship.
78
77
 
79
78
  ## Example Workflow
80
79
 
@@ -86,8 +85,7 @@ The AI assistant calls these tools behind the scenes:
86
85
  2. `decantr_resolve_archetype` -- pulls default pages, layouts, and features for a SaaS dashboard
87
86
  3. `decantr_suggest_patterns` -- recommends `kpi-grid`, `chart-grid`, `data-table`, and `form-sections` for the described pages
88
87
  4. `decantr_resolve_pattern` -- fetches layout specs and component lists for each pattern
89
- 5. `decantr_resolve_recipe` -- loads decoration rules (spacing, borders, effects) for the theme
90
- 6. `decantr_check_drift` -- validates the generated code against the Essence spec before presenting it
88
+ 5. `decantr_check_drift` -- validates the generated code against the Essence spec before presenting it
91
89
 
92
90
  The AI now generates code with the right layout structure, correct components, and consistent styling -- not a generic guess.
93
91
 
package/dist/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-A5IEC7O2.js";
2
+ import "./chunk-YOFQYTEC.js";
@@ -893,12 +893,12 @@ var TOOLS = [
893
893
  {
894
894
  name: "decantr_search_registry",
895
895
  title: "Search Registry",
896
- description: "Search the Decantr community content registry for patterns, archetypes, recipes, and styles.",
896
+ description: "Search the Decantr community content registry for patterns, archetypes, themes, and shells.",
897
897
  inputSchema: {
898
898
  type: "object",
899
899
  properties: {
900
900
  query: { type: "string", description: 'Search query (e.g. "kanban", "neon", "dashboard")' },
901
- type: { type: "string", description: "Filter by type: pattern, archetype, recipe, style" }
901
+ type: { type: "string", description: "Filter by type: pattern, archetype, theme, shell" }
902
902
  },
903
903
  required: ["query"]
904
904
  },
@@ -935,22 +935,7 @@ var TOOLS = [
935
935
  },
936
936
  annotations: READ_ONLY_NETWORK
937
937
  },
938
- // 6. decantr_resolve_recipe — network
939
- {
940
- name: "decantr_resolve_recipe",
941
- title: "Resolve Recipe",
942
- description: "Get recipe visual treatment overrides, decoration rules, shell styles, spatial hints, visual effects, and pattern preferences.",
943
- inputSchema: {
944
- type: "object",
945
- properties: {
946
- id: { type: "string", description: 'Recipe ID (e.g. "auradecantism")' },
947
- namespace: { type: "string", description: 'Namespace (default: "@official")' }
948
- },
949
- required: ["id"]
950
- },
951
- annotations: READ_ONLY_NETWORK
952
- },
953
- // 7. decantr_resolve_blueprint — network
938
+ // 6. decantr_resolve_blueprint — network
954
939
  {
955
940
  name: "decantr_resolve_blueprint",
956
941
  title: "Resolve Blueprint",
@@ -1075,7 +1060,7 @@ var TOOLS = [
1075
1060
  {
1076
1061
  name: "decantr_get_section_context",
1077
1062
  title: "Get Section Context",
1078
- description: "Get the self-contained context for a specific section of the project. Returns guard rules, theme tokens, visual treatments, recipe decorators, pattern specs, zone context, and pages \u2014 everything an AI needs to work on that section.",
1063
+ description: "Get the self-contained context for a specific section of the project. Returns guard rules, theme tokens, visual treatments, pattern specs, zone context, and pages \u2014 everything an AI needs to work on that section.",
1079
1064
  inputSchema: {
1080
1065
  type: "object",
1081
1066
  properties: {
@@ -1106,6 +1091,20 @@ var TOOLS = [
1106
1091
  required: ["component"]
1107
1092
  },
1108
1093
  annotations: READ_ONLY
1094
+ },
1095
+ // 15. decantr_critique — local read
1096
+ {
1097
+ name: "decantr_critique",
1098
+ title: "Design Critique",
1099
+ description: "Evaluate generated code against the essence spec for visual quality. Returns a scorecard covering treatment usage, decorator coverage, personality alignment, motion, accessibility, and responsiveness.",
1100
+ inputSchema: {
1101
+ type: "object",
1102
+ properties: {
1103
+ file_path: { type: "string", description: "Path to the component file to critique" }
1104
+ },
1105
+ required: ["file_path"]
1106
+ },
1107
+ annotations: READ_ONLY
1109
1108
  }
1110
1109
  ];
1111
1110
  async function handleTool(name, args) {
@@ -1206,17 +1205,6 @@ async function handleTool(name, args) {
1206
1205
  return { found: false, message: `Archetype "${args.id}" not found in ${namespace}.` };
1207
1206
  }
1208
1207
  }
1209
- case "decantr_resolve_recipe": {
1210
- const err = validateStringArg(args, "id");
1211
- if (err) return { error: err };
1212
- const namespace = args.namespace || "@official";
1213
- try {
1214
- const recipe = await apiClient.getRecipe(namespace, args.id);
1215
- return { found: true, ...recipe };
1216
- } catch {
1217
- return { found: false, message: `Recipe "${args.id}" not found in ${namespace}.` };
1218
- }
1219
- }
1220
1208
  case "decantr_resolve_blueprint": {
1221
1209
  const err = validateStringArg(args, "id");
1222
1210
  if (err) return { error: err };
@@ -1224,14 +1212,14 @@ async function handleTool(name, args) {
1224
1212
  try {
1225
1213
  const blueprint = await apiClient.getBlueprint(namespace, args.id);
1226
1214
  let topology = null;
1227
- const composeEntries = blueprint.compose || blueprint.data?.compose;
1215
+ const composeEntries = blueprint.compose;
1228
1216
  if (composeEntries && Array.isArray(composeEntries) && composeEntries.length > 0) {
1229
1217
  const zoneInputs = [];
1230
1218
  const archetypePromises = composeEntries.map(async (entry) => {
1231
1219
  const arcId = typeof entry === "string" ? entry : entry.archetype;
1232
1220
  try {
1233
1221
  const arch = await apiClient.getContent("archetypes", namespace, arcId);
1234
- const archData = arch.data || arch;
1222
+ const archData = arch;
1235
1223
  const explicitRole = typeof entry === "object" ? entry.role : void 0;
1236
1224
  zoneInputs.push({
1237
1225
  archetypeId: arcId,
@@ -1278,50 +1266,72 @@ async function handleTool(name, args) {
1278
1266
  namespace: "@official",
1279
1267
  limit: 100
1280
1268
  });
1281
- const suggestions = [];
1269
+ const prelimScores = [];
1282
1270
  for (const p of patternsResponse.items) {
1283
- const searchable = [
1284
- p.name || "",
1285
- p.description || "",
1286
- ...p.components || [],
1287
- ...p.tags || []
1288
- ].join(" ").toLowerCase();
1271
+ const slug = p.slug || "";
1272
+ const name2 = p.name || slug;
1273
+ const description = p.description || "";
1274
+ const searchable = [name2, description].join(" ").toLowerCase();
1289
1275
  let score = 0;
1290
1276
  const words = desc.split(/\s+/);
1291
1277
  for (const word of words) {
1292
1278
  if (word.length < 3) continue;
1293
1279
  if (searchable.includes(word)) score += 10;
1294
1280
  }
1295
- if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(p.id)) score += 20;
1296
- if (desc.includes("metric") && p.id === "kpi-grid") score += 15;
1297
- if (desc.includes("chart") && p.id === "chart-grid") score += 15;
1298
- if (desc.includes("table") && p.id === "data-table") score += 15;
1299
- if (desc.includes("form") && p.id === "form-sections") score += 15;
1300
- if (desc.includes("setting") && p.id === "form-sections") score += 15;
1301
- if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(p.id)) score += 20;
1302
- if (desc.includes("hero") && p.id === "hero") score += 20;
1303
- if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(p.id)) score += 15;
1304
- if (desc.includes("product") && p.id === "card-grid") score += 15;
1305
- if (desc.includes("feed") && p.id === "activity-feed") score += 15;
1306
- if (desc.includes("filter") && p.id === "filter-bar") score += 15;
1307
- if (desc.includes("search") && p.id === "filter-bar") score += 10;
1281
+ if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(slug)) score += 20;
1282
+ if (desc.includes("metric") && slug === "kpi-grid") score += 15;
1283
+ if (desc.includes("chart") && slug === "chart-grid") score += 15;
1284
+ if (desc.includes("table") && slug === "data-table") score += 15;
1285
+ if (desc.includes("form") && slug === "form-sections") score += 15;
1286
+ if (desc.includes("setting") && slug === "form-sections") score += 15;
1287
+ if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(slug)) score += 20;
1288
+ if (desc.includes("hero") && slug === "hero") score += 20;
1289
+ if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(slug)) score += 15;
1290
+ if (desc.includes("product") && slug === "card-grid") score += 15;
1291
+ if (desc.includes("feed") && slug === "activity-feed") score += 15;
1292
+ if (desc.includes("filter") && slug === "filter-bar") score += 15;
1293
+ if (desc.includes("search") && slug === "filter-bar") score += 10;
1308
1294
  if (score > 0) {
1309
- const preset = p.presets ? Object.values(p.presets)[0] : null;
1310
- suggestions.push({
1311
- id: p.id,
1312
- score,
1313
- name: p.name || p.id,
1314
- description: p.description || "",
1315
- components: p.components || [],
1316
- layout: preset?.layout ? preset.layout.layout : "grid"
1317
- });
1295
+ prelimScores.push({ slug, score, name: name2, description });
1318
1296
  }
1319
1297
  }
1298
+ prelimScores.sort((a, b) => b.score - a.score);
1299
+ const top10 = prelimScores.slice(0, 10);
1300
+ const suggestions = [];
1301
+ for (const candidate of top10) {
1302
+ let fullPattern = null;
1303
+ try {
1304
+ const fetched = await apiClient.getPattern("@official", candidate.slug);
1305
+ fullPattern = fetched;
1306
+ } catch {
1307
+ }
1308
+ let score = candidate.score;
1309
+ if (fullPattern) {
1310
+ const fullSearchable = [
1311
+ ...fullPattern.components || [],
1312
+ ...fullPattern.tags || []
1313
+ ].join(" ").toLowerCase();
1314
+ const words = desc.split(/\s+/);
1315
+ for (const word of words) {
1316
+ if (word.length < 3) continue;
1317
+ if (fullSearchable.includes(word)) score += 10;
1318
+ }
1319
+ }
1320
+ const preset = fullPattern?.presets ? Object.values(fullPattern.presets)[0] : null;
1321
+ suggestions.push({
1322
+ id: candidate.slug,
1323
+ score,
1324
+ name: fullPattern?.name || candidate.name,
1325
+ description: fullPattern?.description || candidate.description,
1326
+ components: fullPattern?.components || [],
1327
+ layout: preset?.layout ? preset.layout.layout : "grid"
1328
+ });
1329
+ }
1320
1330
  suggestions.sort((a, b) => b.score - a.score);
1321
1331
  return {
1322
1332
  query: args.description,
1323
1333
  suggestions: suggestions.slice(0, 5),
1324
- total: suggestions.length
1334
+ total: prelimScores.length
1325
1335
  };
1326
1336
  } catch (e) {
1327
1337
  return { error: `Could not fetch patterns: ${e.message}` };
@@ -1343,10 +1353,10 @@ async function handleTool(name, args) {
1343
1353
  if (args.theme_used && typeof args.theme_used === "string") {
1344
1354
  let expectedStyle;
1345
1355
  if (isV32(essence)) {
1346
- expectedStyle = essence.dna.theme.style;
1356
+ expectedStyle = essence.dna.theme.id;
1347
1357
  } else {
1348
1358
  const expectedTheme = essence.theme;
1349
- expectedStyle = expectedTheme?.style;
1359
+ expectedStyle = expectedTheme?.id ?? expectedTheme?.style;
1350
1360
  }
1351
1361
  if (expectedStyle && args.theme_used !== expectedStyle) {
1352
1362
  violations.push({
@@ -1504,9 +1514,8 @@ async function handleTool(name, args) {
1504
1514
  version: "3.0.0",
1505
1515
  dna: {
1506
1516
  theme: {
1507
- style: "auradecantism",
1517
+ id: "auradecantism",
1508
1518
  mode: "dark",
1509
- recipe: "auradecantism",
1510
1519
  shape: "rounded"
1511
1520
  },
1512
1521
  spacing: {
@@ -1585,14 +1594,14 @@ async function handleTool(name, args) {
1585
1594
  }
1586
1595
  const hasDnaViolation = violations.some((v) => {
1587
1596
  const rule = v.rule;
1588
- return ["style", "recipe", "density", "theme-mode", "accessibility", "theme-match"].includes(rule);
1597
+ return ["style", "density", "theme-mode", "accessibility", "theme-match"].includes(rule);
1589
1598
  });
1590
1599
  if (hasDnaViolation && resolution !== "reject" && resolution !== "defer" && !args.confirm_dna) {
1591
1600
  return {
1592
1601
  error: "DNA-layer violations detected. Set confirm_dna: true to accept changes to design axioms (theme, style, density, etc.).",
1593
1602
  requires_confirmation: true,
1594
1603
  dna_rules_affected: violations.filter(
1595
- (v) => ["style", "recipe", "density", "theme-mode", "accessibility", "theme-match"].includes(v.rule)
1604
+ (v) => ["style", "density", "theme-mode", "accessibility", "theme-match"].includes(v.rule)
1596
1605
  ).map((v) => v.rule)
1597
1606
  };
1598
1607
  }
@@ -1773,6 +1782,11 @@ async function handleTool(name, args) {
1773
1782
  }
1774
1783
  return { content: lines.join("\n") };
1775
1784
  }
1785
+ case "decantr_critique": {
1786
+ const { critiqueFile } = await import("./critique-DKY4NB2O.js");
1787
+ const result = await critiqueFile(args.file_path, process.cwd());
1788
+ return result;
1789
+ }
1776
1790
  default:
1777
1791
  return { error: `Unknown tool: ${name}` };
1778
1792
  }
@@ -1782,7 +1796,7 @@ function applyDriftAcceptance(essence, violation, resolution, scope) {
1782
1796
  case "theme-match":
1783
1797
  case "style": {
1784
1798
  if (violation.details) {
1785
- essence.dna.theme.style = violation.details;
1799
+ essence.dna.theme.id = violation.details;
1786
1800
  }
1787
1801
  break;
1788
1802
  }
@@ -1802,12 +1816,6 @@ function applyDriftAcceptance(essence, violation, resolution, scope) {
1802
1816
  case "layout": {
1803
1817
  break;
1804
1818
  }
1805
- case "recipe": {
1806
- if (violation.details) {
1807
- essence.dna.theme.recipe = violation.details;
1808
- }
1809
- break;
1810
- }
1811
1819
  case "density": {
1812
1820
  break;
1813
1821
  }
@@ -0,0 +1,74 @@
1
+ // src/critique.ts
2
+ import { readFile } from "fs/promises";
3
+ import { join } from "path";
4
+ async function critiqueFile(filePath, projectRoot) {
5
+ const code = await readFile(filePath, "utf-8");
6
+ const codeLower = code.toLowerCase();
7
+ let treatments = "";
8
+ try {
9
+ treatments = await readFile(join(projectRoot, "src", "styles", "treatments.css"), "utf-8");
10
+ } catch {
11
+ }
12
+ const scores = [];
13
+ const treatmentClasses = ["d-interactive", "d-surface", "d-data", "d-control", "d-section", "d-annotation", "d-label"];
14
+ const usedTreatments = treatmentClasses.filter((t) => code.includes(t));
15
+ scores.push({
16
+ category: "Treatment Usage",
17
+ score: Math.min(5, Math.max(1, Math.round(usedTreatments.length / treatmentClasses.length * 5))),
18
+ details: `${usedTreatments.length}/${treatmentClasses.length} base treatments used: ${usedTreatments.join(", ") || "none"}`,
19
+ suggestions: treatmentClasses.filter((t) => !code.includes(t)).map((t) => `Consider using \`${t}\` where appropriate`)
20
+ });
21
+ const decoratorMatches = treatments.match(/\.([\w-]+)\s*\{/g) || [];
22
+ const decoratorNames = decoratorMatches.map((m) => m.slice(1).replace(/\s*\{$/, "")).filter((n) => !n.startsWith("d-") && !["neon-glow", "neon-glow-hover", "neon-text-glow", "neon-border-glow", "mono-data", "status-ring", "entrance-fade"].includes(n));
23
+ const usedDecorators = decoratorNames.filter((d) => code.includes(d));
24
+ const decTarget = Math.min(decoratorNames.length, 5);
25
+ scores.push({
26
+ category: "Decorator Usage",
27
+ score: decTarget > 0 ? Math.min(5, Math.max(1, Math.round(usedDecorators.length / decTarget * 5))) : 3,
28
+ details: `${usedDecorators.length}/${decoratorNames.length} theme decorators used`,
29
+ suggestions: decoratorNames.filter((d) => !code.includes(d)).slice(0, 3).map((d) => `Theme decorator \`${d}\` available but unused`)
30
+ });
31
+ const personalityUtils = ["neon-glow", "neon-text-glow", "neon-border-glow", "mono-data", "status-ring", "entrance-fade"];
32
+ const availableUtils = personalityUtils.filter((u) => treatments.includes(u));
33
+ const usedUtils = availableUtils.filter((u) => code.includes(u));
34
+ const utilTarget = Math.min(availableUtils.length, 3);
35
+ scores.push({
36
+ category: "Personality Alignment",
37
+ score: utilTarget > 0 ? Math.min(5, Math.max(1, Math.round(usedUtils.length / utilTarget * 5))) : 3,
38
+ details: `${usedUtils.length}/${availableUtils.length} personality utilities used`,
39
+ suggestions: availableUtils.filter((u) => !code.includes(u)).map((u) => `Personality utility \`${u}\` defined but not used`)
40
+ });
41
+ const hasTransition = codeLower.includes("transition") || codeLower.includes("animate") || codeLower.includes("keyframe") || codeLower.includes("framer");
42
+ const hasHover = codeLower.includes(":hover") || codeLower.includes("onmouseenter") || codeLower.includes("hover:");
43
+ scores.push({
44
+ category: "Motion & Interaction",
45
+ score: Math.min(5, (hasTransition ? 3 : 1) + (hasHover ? 1 : 0)),
46
+ details: `Transitions: ${hasTransition ? "yes" : "no"}, Hover states: ${hasHover ? "yes" : "no"}`,
47
+ suggestions: hasTransition ? [] : ["Add transition effects for interactive elements"]
48
+ });
49
+ const hasAria = codeLower.includes("aria-") || codeLower.includes("role=");
50
+ const hasFocus = codeLower.includes("focus-visible") || codeLower.includes("focusvisible");
51
+ const hasKeyboard = codeLower.includes("onkeydown") || codeLower.includes("onkeyup");
52
+ scores.push({
53
+ category: "Accessibility",
54
+ score: Math.min(5, 1 + (hasAria ? 2 : 0) + (hasFocus ? 1 : 0) + (hasKeyboard ? 1 : 0)),
55
+ details: `ARIA: ${hasAria}, Focus: ${hasFocus}, Keyboard: ${hasKeyboard}`,
56
+ suggestions: [
57
+ ...!hasAria ? ["Add ARIA attributes to interactive regions"] : [],
58
+ ...!hasKeyboard ? ["Add keyboard event handlers"] : []
59
+ ]
60
+ });
61
+ const hasMedia = codeLower.includes("@media") || codeLower.includes("usemediaquery") || codeLower.includes("breakpoint");
62
+ const hasResponsive = codeLower.includes("sm:") || codeLower.includes("md:") || codeLower.includes("lg:") || codeLower.includes("_sm_") || codeLower.includes("_md_");
63
+ scores.push({
64
+ category: "Responsive Design",
65
+ score: Math.min(5, (hasMedia ? 3 : 1) + (hasResponsive ? 2 : 0)),
66
+ details: `Media queries: ${hasMedia}, Responsive classes: ${hasResponsive}`,
67
+ suggestions: !hasMedia && !hasResponsive ? ["Add responsive breakpoint handling"] : []
68
+ });
69
+ const overall = Math.round(scores.reduce((s, c) => s + c.score, 0) / scores.length * 10) / 10;
70
+ return { file: filePath, overall, scores };
71
+ }
72
+ export {
73
+ critiqueFile
74
+ };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import "./chunk-A5IEC7O2.js";
1
+ import "./chunk-YOFQYTEC.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/mcp-server",
3
- "version": "1.0.0-beta.10",
3
+ "version": "1.0.0-beta.12",
4
4
  "description": "MCP server for Decantr — exposes design intelligence tools to AI coding assistants",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,18 +15,20 @@
15
15
  },
16
16
  "main": "dist/index.js",
17
17
  "types": "dist/index.d.ts",
18
- "files": ["dist"],
18
+ "files": [
19
+ "dist"
20
+ ],
19
21
  "publishConfig": {
20
22
  "access": "public"
21
23
  },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.0.0",
26
+ "@decantr/essence-spec": "1.0.0-beta.11",
27
+ "@decantr/registry": "1.0.0-beta.11"
28
+ },
22
29
  "scripts": {
23
30
  "build": "tsup",
24
31
  "test": "vitest run",
25
32
  "test:watch": "vitest"
26
- },
27
- "dependencies": {
28
- "@modelcontextprotocol/sdk": "^1.0.0",
29
- "@decantr/essence-spec": "workspace:*",
30
- "@decantr/registry": "workspace:*"
31
33
  }
32
- }
34
+ }