@decantr/mcp-server 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@ Design intelligence for AI-generated UI. Make Claude, Cursor, and Windsurf gener
12
12
 
13
13
  ![Decantr MCP demo](https://raw.githubusercontent.com/decantr-ai/decantr/main/packages/mcp-server/assets/decantr-demo.gif)
14
14
 
15
- - **Structured design context** -- gives your AI assistant patterns, layouts, and component specs instead of letting it guess
15
+ - **Structured design context** -- gives your AI assistant patterns, layouts, component specs, Brownfield local law, and task-time context instead of letting it guess
16
16
  - **Evidence-backed repair loops** -- gives AI agents Project Health, Evidence Bundles, workspace health, and scoped repair prompts without uploading source
17
17
  - **Drift detection** -- catches when generated code deviates from your design intent
18
18
  - **Zero config** -- run with `npx`, no API keys or accounts required
@@ -133,9 +133,10 @@ The server exposes Decantr registry, context, benchmark, and verification tools.
133
133
  | `decantr_resolve_pattern` | Get full pattern details: layout spec, components, presets, code examples | `{ "id": "data-table", "preset": "product" }` |
134
134
  | `decantr_resolve_archetype` | Get archetype details: default pages, layouts, features, suggested theme | `{ "id": "saas-dashboard" }` |
135
135
  | `decantr_resolve_blueprint` | Get a full app composition with page structure and personality traits | `{ "id": "ecommerce" }` |
136
- | `decantr_suggest_patterns` | Given a page description, get ranked pattern suggestions | `{ "description": "dashboard with metrics and charts" }` |
136
+ | `decantr_suggest_patterns` | Given a page description plus optional route/source excerpt, get ranked pattern suggestions | `{ "description": "recipe feed with avatars and infinite scroll", "route": "/feed" }` |
137
137
  | `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" }` |
138
138
  | `decantr_get_execution_pack` | Read compiled scaffold, section, page, review, or mutation execution packs, with hosted fallback when local context is missing | `{ "pack_type": "page", "id": "overview", "format": "json" }` |
139
+ | `decantr_prepare_task_context` | Resolve compact route/task context, local law, evidence, and changed-file impact before editing a Brownfield or Essence route | `{ "route": "/feed", "task": "improve recipe card loading" }` |
139
140
  | `decantr_compile_execution_packs` | Compile a hosted execution-pack bundle from a local or inline essence document | `{ "path": "./decantr.essence.json", "namespace": "@official" }` |
140
141
  | `decantr_audit_project` | Run the schema-backed Decantr project audit against essence and compiled packs, with hosted fallback when local pack artifacts are missing | `{ "namespace": "@official" }` |
141
142
  | `decantr_critique` | Critique a file against the compiled review contract, with hosted fallback when local review packs are missing | `{ "file_path": "./src/pages/Overview.tsx", "namespace": "@official" }` |
@@ -170,12 +171,13 @@ The AI assistant calls these tools behind the scenes:
170
171
  3. `decantr_suggest_patterns` -- recommends `kpi-grid`, `chart-grid`, `data-table`, and `form-sections` for the described pages
171
172
  4. `decantr_resolve_pattern` -- fetches layout specs and component lists for each pattern
172
173
  5. `decantr_get_execution_pack` -- loads the compiled scaffold/page/review packs as the task contract, falling back to hosted compilation when local pack artifacts are missing
173
- 6. `decantr_compile_execution_packs` -- compiles the hosted pack bundle when the task needs a fresh remote contract from the essence document
174
- 7. `decantr_check_drift` -- validates the generated code against the Essence spec before presenting it
175
- 8. `decantr_critique` -- critiques a specific file, falling back to the hosted verifier when the local review pack is missing
176
- 9. `decantr_audit_project` -- runs the stronger project-level audit once the implementation is in place
177
- 10. `decantr_get_evidence_bundle` -- returns the local evidence bundle for the AI repair loop
178
- 11. `decantr_get_repair_prompt` -- gives the assistant exact finding evidence, constraints to preserve, and commands to rerun
174
+ 6. `decantr_prepare_task_context` -- resolves route-local Brownfield context, accepted local law, changed-file impact, visual evidence, and theme inventory before editing an existing app
175
+ 7. `decantr_compile_execution_packs` -- compiles the hosted pack bundle when the task needs a fresh remote contract from the essence document
176
+ 8. `decantr_check_drift` -- validates the generated code against the Essence spec before presenting it
177
+ 9. `decantr_critique` -- critiques a specific file, falling back to the hosted verifier when the local review pack is missing
178
+ 10. `decantr_audit_project` -- runs the stronger project-level audit once the implementation is in place
179
+ 11. `decantr_get_evidence_bundle` -- returns the local evidence bundle for the AI repair loop
180
+ 12. `decantr_get_repair_prompt` -- gives the assistant exact finding evidence, constraints to preserve, and commands to rerun
179
181
 
180
182
  The AI now generates code with the right layout structure, correct components, and consistent styling, then gets a scoped evidence-backed repair loop instead of a generic guess.
181
183
 
package/dist/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-FEXPLJKB.js";
2
+ import "./chunk-GIVGJE76.js";
@@ -4,11 +4,17 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
5
5
 
6
6
  // src/tools.ts
7
+ import { execFileSync } from "child_process";
7
8
  import { existsSync, readdirSync, readFileSync } from "fs";
8
9
  import { readFile as readFile2 } from "fs/promises";
9
10
  import { basename as basename2, dirname as dirname2, join as join2, relative as relative2 } from "path";
10
11
  import { evaluateGuard, isV4 as isV42, validateEssence } from "@decantr/essence-spec";
11
- import { isContentIntelligenceSource, resolvePatternPreset } from "@decantr/registry";
12
+ import {
13
+ isContentIntelligenceSource,
14
+ patternToDiscoveryCandidate,
15
+ rankPatternCandidates,
16
+ resolvePatternPreset
17
+ } from "@decantr/registry";
12
18
 
13
19
  // src/helpers.ts
14
20
  import { realpathSync } from "fs";
@@ -124,6 +130,102 @@ async function writeDriftLog(entries, projectRoot) {
124
130
  }
125
131
 
126
132
  // src/tools.ts
133
+ function readJsonIfExists(path) {
134
+ if (!existsSync(path)) return null;
135
+ try {
136
+ return JSON.parse(readFileSync(path, "utf-8"));
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+ function changedFilesForTask(projectRoot) {
142
+ const changed = /* @__PURE__ */ new Set();
143
+ try {
144
+ for (const args of [
145
+ ["diff", "--name-only"],
146
+ ["diff", "--name-only", "--cached"]
147
+ ]) {
148
+ const output = execFileSync("git", args, {
149
+ cwd: projectRoot,
150
+ encoding: "utf-8",
151
+ stdio: ["ignore", "pipe", "ignore"]
152
+ });
153
+ for (const entry of output.split(/\r?\n/)) {
154
+ const file = entry.trim();
155
+ if (file) changed.add(file);
156
+ }
157
+ }
158
+ } catch {
159
+ }
160
+ return [...changed].sort();
161
+ }
162
+ function impactedRoutesForFiles(projectRoot, files) {
163
+ const analysis = readJsonIfExists(
164
+ join2(projectRoot, ".decantr", "analysis.json")
165
+ );
166
+ const routeEntries = analysis?.routes?.routes ?? [];
167
+ const impacted = /* @__PURE__ */ new Set();
168
+ for (const file of files) {
169
+ for (const route of routeEntries) {
170
+ if (route.file && (file === route.file || file.endsWith(route.file))) {
171
+ if (route.path) impacted.add(route.path);
172
+ }
173
+ }
174
+ }
175
+ return [...impacted].sort();
176
+ }
177
+ function localLawSummary(projectRoot) {
178
+ const patterns = readJsonIfExists(join2(projectRoot, ".decantr", "local-patterns.json"));
179
+ const rules = readJsonIfExists(join2(projectRoot, ".decantr", "rules.json"));
180
+ return {
181
+ patterns_path: patterns ? ".decantr/local-patterns.json" : null,
182
+ rules_path: rules ? ".decantr/rules.json" : null,
183
+ patterns: patterns?.patterns?.map((pattern) => ({
184
+ id: pattern.id ?? "unknown",
185
+ role: pattern.role ?? null,
186
+ component_paths: pattern.componentPaths ?? []
187
+ })) ?? [],
188
+ rules: rules?.rules?.map((rule) => ({
189
+ id: rule.id ?? "unknown",
190
+ enabled: rule.enabled ?? false,
191
+ severity: rule.severity ?? "warn",
192
+ description: rule.description ?? null
193
+ })) ?? []
194
+ };
195
+ }
196
+ function extractPatternIdsFromLayoutItem(item, ids) {
197
+ if (typeof item === "string") {
198
+ ids.add(item);
199
+ return;
200
+ }
201
+ if (!item || typeof item !== "object") return;
202
+ const record = item;
203
+ if (typeof record.pattern === "string") ids.add(record.pattern);
204
+ if (Array.isArray(record.cols)) {
205
+ for (const col of record.cols) extractPatternIdsFromLayoutItem(col, ids);
206
+ }
207
+ }
208
+ function extractPagePatternIds(page) {
209
+ if (!page) return [];
210
+ const ids = /* @__PURE__ */ new Set();
211
+ for (const item of page.layout ?? []) extractPatternIdsFromLayoutItem(item, ids);
212
+ return [...ids].sort();
213
+ }
214
+ function summarizePackJson(pack) {
215
+ if (!pack || typeof pack !== "object") {
216
+ return { directives: [], patterns: [], visualTarget: null, sharedComponents: [] };
217
+ }
218
+ const record = pack;
219
+ const data = record.data && typeof record.data === "object" ? record.data : record;
220
+ const patterns = Array.isArray(data.patterns) ? data.patterns : [];
221
+ const directives = Array.isArray(data.directives) ? data.directives : [];
222
+ const sharedComponents = Array.isArray(data.sharedComponents) ? data.sharedComponents : Array.isArray(data.shared_components) ? data.shared_components : [];
223
+ const visualTarget = typeof data.visualTarget === "string" ? data.visualTarget : typeof data.visual_target === "string" ? data.visual_target : null;
224
+ return { directives, patterns, visualTarget, sharedComponents };
225
+ }
226
+ function routeSlug(route) {
227
+ return route.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "root";
228
+ }
127
229
  async function getShowcaseBenchmarkPayload(view) {
128
230
  const client = getPublicAPIClient();
129
231
  if (view === "manifest") {
@@ -490,7 +592,10 @@ function mcpStatusFromCounts(counts) {
490
592
  return "healthy";
491
593
  }
492
594
  function mcpScoreFromCounts(counts) {
493
- return Math.max(0, Math.min(100, 100 - counts.errorCount * 15 - counts.warnCount * 5 - counts.infoCount));
595
+ return Math.max(
596
+ 0,
597
+ Math.min(100, 100 - counts.errorCount * 15 - counts.warnCount * 5 - counts.infoCount)
598
+ );
494
599
  }
495
600
  function mcpCommandsForFinding(source) {
496
601
  switch (source) {
@@ -507,7 +612,11 @@ function mcpCommandsForFinding(source) {
507
612
  case "interaction":
508
613
  return ["decantr check --strict", "decantr health"];
509
614
  case "pack":
510
- return ["decantr refresh", "decantr registry get-pack review --write-context", "decantr health"];
615
+ return [
616
+ "decantr refresh",
617
+ "decantr registry get-pack review --write-context",
618
+ "decantr health"
619
+ ];
511
620
  case "runtime":
512
621
  return ["npm run build", "decantr health"];
513
622
  default:
@@ -908,6 +1017,14 @@ var TOOLS = [
908
1017
  description: {
909
1018
  type: "string",
910
1019
  description: 'Description of the page or section (e.g. "dashboard with metrics and charts", "settings form with toggles")'
1020
+ },
1021
+ route: {
1022
+ type: "string",
1023
+ description: 'Optional route context, for example "/feed" or "/settings".'
1024
+ },
1025
+ source_code: {
1026
+ type: "string",
1027
+ description: "Optional local source excerpt to rank against actual code evidence."
911
1028
  }
912
1029
  },
913
1030
  required: ["description"]
@@ -1105,7 +1222,31 @@ var TOOLS = [
1105
1222
  },
1106
1223
  annotations: READ_ONLY
1107
1224
  },
1108
- // 16. decantr_get_execution_pack — local read
1225
+ // 16. decantr_prepare_task_context — local read
1226
+ {
1227
+ name: "decantr_prepare_task_context",
1228
+ title: "Prepare Task Context",
1229
+ description: "Resolve compact Brownfield/Essence task-time context for a route or page before editing. Returns route, section, page pack, directives, patterns, shared components, visual target, health evidence, and local screenshot references when available.",
1230
+ inputSchema: {
1231
+ type: "object",
1232
+ properties: {
1233
+ route: {
1234
+ type: "string",
1235
+ description: 'Route being edited, for example "/feed". Preferred when known.'
1236
+ },
1237
+ page_id: {
1238
+ type: "string",
1239
+ description: "Page ID when route is unknown."
1240
+ },
1241
+ task: {
1242
+ type: "string",
1243
+ description: "Short task description used to rank relevant patterns and context."
1244
+ }
1245
+ }
1246
+ },
1247
+ annotations: READ_ONLY
1248
+ },
1249
+ // 17. decantr_get_execution_pack — local read
1109
1250
  {
1110
1251
  name: "decantr_get_execution_pack",
1111
1252
  title: "Get Execution Pack",
@@ -1506,78 +1647,59 @@ async function handleTool(name, args) {
1506
1647
  case "decantr_suggest_patterns": {
1507
1648
  const err = validateStringArg(args, "description");
1508
1649
  if (err) return { error: err };
1509
- const desc = args.description.toLowerCase();
1650
+ const desc = args.description;
1651
+ const route = typeof args.route === "string" ? args.route : void 0;
1652
+ const sourceCode = typeof args.source_code === "string" ? args.source_code : void 0;
1510
1653
  try {
1511
1654
  const patternsResponse = await apiClient.listContent("patterns", {
1512
1655
  namespace: "@official",
1513
- limit: 100
1656
+ limit: 250
1514
1657
  });
1515
- const prelimScores = [];
1516
- for (const p of patternsResponse.items) {
1517
- const slug = p.slug || "";
1518
- const name2 = p.name || slug;
1519
- const description = p.description || "";
1520
- const searchable = [name2, description].join(" ").toLowerCase();
1521
- let score = 0;
1522
- const words = desc.split(/\s+/);
1523
- for (const word of words) {
1524
- if (word.length < 3) continue;
1525
- if (searchable.includes(word)) score += 10;
1526
- }
1527
- if (desc.includes("dashboard") && ["kpi-grid", "chart-grid", "data-table", "filter-bar"].includes(slug))
1528
- score += 20;
1529
- if (desc.includes("metric") && slug === "kpi-grid") score += 15;
1530
- if (desc.includes("chart") && slug === "chart-grid") score += 15;
1531
- if (desc.includes("table") && slug === "data-table") score += 15;
1532
- if (desc.includes("form") && slug === "form-sections") score += 15;
1533
- if (desc.includes("setting") && slug === "form-sections") score += 15;
1534
- if (desc.includes("landing") && ["hero", "cta-section", "card-grid"].includes(slug))
1535
- score += 20;
1536
- if (desc.includes("hero") && slug === "hero") score += 20;
1537
- if (desc.includes("ecommerce") && ["card-grid", "filter-bar", "detail-header"].includes(slug))
1538
- score += 15;
1539
- if (desc.includes("product") && slug === "card-grid") score += 15;
1540
- if (desc.includes("feed") && slug === "activity-feed") score += 15;
1541
- if (desc.includes("filter") && slug === "filter-bar") score += 15;
1542
- if (desc.includes("search") && slug === "filter-bar") score += 10;
1543
- if (score > 0) {
1544
- prelimScores.push({ slug, score, name: name2, description });
1545
- }
1546
- }
1547
- prelimScores.sort((a, b) => b.score - a.score);
1548
- const top10 = prelimScores.slice(0, 10);
1549
- const suggestions = [];
1550
- for (const candidate of top10) {
1551
- let fullPattern = null;
1552
- try {
1553
- const fetched = await apiClient.getPattern("@official", candidate.slug);
1554
- fullPattern = fetched;
1555
- } catch {
1556
- }
1557
- let score = candidate.score;
1558
- if (fullPattern) {
1559
- const fullSearchable = [...fullPattern.components || [], ...fullPattern.tags || []].join(" ").toLowerCase();
1560
- const words = desc.split(/\s+/);
1561
- for (const word of words) {
1562
- if (word.length < 3) continue;
1563
- if (fullSearchable.includes(word)) score += 10;
1658
+ const preliminary = rankPatternCandidates(
1659
+ { query: desc, route, code: sourceCode, limit: 12 },
1660
+ patternsResponse.items.map(
1661
+ (item) => patternToDiscoveryCandidate({
1662
+ id: item.slug || item.name || "pattern",
1663
+ slug: item.slug,
1664
+ name: item.name,
1665
+ description: item.description
1666
+ })
1667
+ )
1668
+ );
1669
+ const fullCandidates = await Promise.all(
1670
+ preliminary.map(async (match) => {
1671
+ const slug = match.candidate.slug || match.candidate.id;
1672
+ try {
1673
+ const fetched = await apiClient.getPattern("@official", slug);
1674
+ return patternToDiscoveryCandidate(fetched, { slug, source: "hosted" });
1675
+ } catch {
1676
+ return match.candidate;
1564
1677
  }
1565
- }
1566
- const preset = fullPattern?.presets ? Object.values(fullPattern.presets)[0] : null;
1567
- suggestions.push({
1568
- id: candidate.slug,
1569
- score,
1570
- name: fullPattern?.name || candidate.name,
1571
- description: fullPattern?.description || candidate.description,
1572
- components: fullPattern?.components || [],
1573
- layout: preset?.layout ? preset.layout.layout : "grid"
1574
- });
1575
- }
1576
- suggestions.sort((a, b) => b.score - a.score);
1678
+ })
1679
+ );
1680
+ const suggestions = rankPatternCandidates(
1681
+ { query: desc, route, code: sourceCode, limit: 5 },
1682
+ fullCandidates
1683
+ ).map((match) => {
1684
+ const pattern = match.candidate.pattern;
1685
+ const preset = pattern?.presets ? Object.values(pattern.presets)[0] : null;
1686
+ return {
1687
+ id: match.candidate.slug || match.candidate.id,
1688
+ score: match.score,
1689
+ name: match.candidate.name || match.candidate.slug || match.candidate.id,
1690
+ description: match.candidate.description || "",
1691
+ components: match.candidate.components || [],
1692
+ interactions: match.candidate.interactions || [],
1693
+ layout: preset?.layout ? preset.layout.layout : "unknown",
1694
+ reasons: match.reasons,
1695
+ matched_terms: match.matchedTerms
1696
+ };
1697
+ });
1577
1698
  return {
1578
1699
  query: args.description,
1579
- suggestions: suggestions.slice(0, 5),
1580
- total: prelimScores.length
1700
+ route,
1701
+ suggestions,
1702
+ total: preliminary.length
1581
1703
  };
1582
1704
  } catch (e) {
1583
1705
  return { error: `Could not fetch patterns: ${e.message}` };
@@ -2251,6 +2373,130 @@ async function handleTool(name, args) {
2251
2373
  hosted_fallback_error: hostedFallbackError ?? void 0
2252
2374
  };
2253
2375
  }
2376
+ case "decantr_prepare_task_context": {
2377
+ const routeArg = typeof args.route === "string" ? args.route : void 0;
2378
+ const pageArg = typeof args.page_id === "string" ? args.page_id : void 0;
2379
+ const task = typeof args.task === "string" ? args.task : "";
2380
+ if (!routeArg && !pageArg) {
2381
+ return { error: "Provide route or page_id." };
2382
+ }
2383
+ let essence;
2384
+ try {
2385
+ const result = await readEssenceFile();
2386
+ essence = result.essence;
2387
+ } catch {
2388
+ return { error: "No valid essence file found. Run decantr init first." };
2389
+ }
2390
+ if (!isV42(essence)) {
2391
+ return {
2392
+ error: "Task context requires Essence v4.0.0. Run `decantr migrate --to v4` first."
2393
+ };
2394
+ }
2395
+ const routeEntry = routeArg ? essence.blueprint.routes?.[routeArg] : null;
2396
+ const sectionId = routeEntry?.section;
2397
+ const pageId = pageArg || routeEntry?.page;
2398
+ const section = sectionId ? essence.blueprint.sections.find((entry) => entry.id === sectionId) : essence.blueprint.sections.find(
2399
+ (entry) => entry.pages.some((page2) => page2.id === pageId)
2400
+ );
2401
+ const page = section?.pages.find((entry) => entry.id === pageId) ?? null;
2402
+ if (!section || !page || !pageId) {
2403
+ return {
2404
+ error: "Could not resolve route/page to an Essence section page.",
2405
+ available_routes: Object.keys(essence.blueprint.routes ?? {}).sort(),
2406
+ available_pages: essence.blueprint.sections.flatMap(
2407
+ (entry) => entry.pages.map((pageEntry) => ({ section_id: entry.id, page_id: pageEntry.id }))
2408
+ )
2409
+ };
2410
+ }
2411
+ const contextDir = join2(process.cwd(), ".decantr", "context");
2412
+ const manifest = readJsonIfExists(join2(contextDir, "pack-manifest.json"));
2413
+ const pageManifest = manifest?.pages.find((entry) => entry.id === pageId) ?? null;
2414
+ const sectionManifest = manifest?.sections.find((entry) => entry.id === section.id) ?? null;
2415
+ const pagePackJson = pageManifest ? readJsonIfExists(join2(contextDir, pageManifest.json)) : null;
2416
+ const sectionPackJson = sectionManifest ? readJsonIfExists(join2(contextDir, sectionManifest.json)) : null;
2417
+ const pagePackMarkdown = pageManifest && existsSync(join2(contextDir, pageManifest.markdown)) ? readFileSync(join2(contextDir, pageManifest.markdown), "utf-8") : null;
2418
+ const sectionContextPath = join2(contextDir, `section-${section.id}.md`);
2419
+ const sectionContext = existsSync(sectionContextPath) ? readFileSync(sectionContextPath, "utf-8") : null;
2420
+ const pagePackSummary = summarizePackJson(pagePackJson);
2421
+ const sectionPackSummary = summarizePackJson(sectionPackJson);
2422
+ const visualManifest = readJsonIfExists(join2(process.cwd(), ".decantr", "evidence", "visual-manifest.json"));
2423
+ const visualRoute = visualManifest?.routes?.find((entry) => entry.route === routeArg) ?? visualManifest?.routes?.find(
2424
+ (entry) => entry.screenshot?.includes(routeSlug(routeArg ?? pageId))
2425
+ ) ?? null;
2426
+ const health = readJsonIfExists(join2(process.cwd(), ".decantr", "health-baseline-diff.json"));
2427
+ const themeInventory = readJsonIfExists(
2428
+ join2(process.cwd(), ".decantr", "theme-inventory.json")
2429
+ );
2430
+ const localLaw = localLawSummary(process.cwd());
2431
+ const changedFiles = changedFilesForTask(process.cwd());
2432
+ const changedRoutes = impactedRoutesForFiles(process.cwd(), changedFiles);
2433
+ const patternIds = extractPagePatternIds(page);
2434
+ const ranked = rankPatternCandidates(
2435
+ {
2436
+ query: [task, routeArg, page.description, ...patternIds].filter(Boolean).join(" "),
2437
+ limit: 5
2438
+ },
2439
+ patternIds.map((id) => patternToDiscoveryCandidate({ id, name: id, description: id }))
2440
+ );
2441
+ return {
2442
+ route: routeArg ?? null,
2443
+ page_id: pageId,
2444
+ section_id: section.id,
2445
+ section_role: section.role,
2446
+ shell: section.shell,
2447
+ task,
2448
+ visual_target: pagePackSummary.visualTarget ?? sectionPackSummary.visualTarget ?? essence.dna.personality?.join(". ") ?? null,
2449
+ directives: pagePackSummary.directives,
2450
+ patterns: pagePackSummary.patterns.length > 0 ? pagePackSummary.patterns : patternIds,
2451
+ ranked_patterns: ranked.map((match) => ({
2452
+ id: match.candidate.slug || match.candidate.id,
2453
+ score: match.score,
2454
+ reasons: match.reasons
2455
+ })),
2456
+ shared_components: pagePackSummary.sharedComponents,
2457
+ section_context: sectionContext,
2458
+ page_pack_excerpt: pagePackMarkdown ? pagePackMarkdown.slice(0, 12e3) : null,
2459
+ health_evidence: health ? {
2460
+ baseline_path: health.baselinePath,
2461
+ saved_at: health.savedAt,
2462
+ status_changed: health.statusChanged,
2463
+ score_delta: health.scoreDelta,
2464
+ added_findings: health.addedFindings?.slice(0, 8) ?? [],
2465
+ resolved_findings: health.resolvedFindings?.slice(0, 8) ?? [],
2466
+ changed_routes: health.changedRoutes ?? [],
2467
+ changed_screenshots: health.changedScreenshots ?? [],
2468
+ contract_drift: health.contractDrift ?? []
2469
+ } : null,
2470
+ visual_evidence: visualRoute ? {
2471
+ screenshot: visualRoute.screenshot ?? null,
2472
+ screenshot_hash: visualRoute.screenshotHash ?? null,
2473
+ status: visualRoute.status ?? null,
2474
+ error: visualRoute.error ?? null
2475
+ } : null,
2476
+ theme_inventory: themeInventory ? {
2477
+ modes: themeInventory.modes,
2478
+ variants: themeInventory.variants,
2479
+ path: ".decantr/theme-inventory.json"
2480
+ } : null,
2481
+ local_law: localLaw,
2482
+ change_impact: {
2483
+ changed_files: changedFiles.slice(0, 40),
2484
+ changed_file_count: changedFiles.length,
2485
+ impacted_routes: changedRoutes
2486
+ },
2487
+ verify_command: "decantr verify --brownfield --local-patterns",
2488
+ local_files: {
2489
+ page_pack: pageManifest?.markdown ?? null,
2490
+ section_pack: sectionManifest?.markdown ?? null,
2491
+ section_context: existsSync(sectionContextPath) ? `.decantr/context/section-${section.id}.md` : null,
2492
+ local_patterns: localLaw.patterns_path,
2493
+ local_rules: localLaw.rules_path,
2494
+ visual_manifest: existsSync(
2495
+ join2(process.cwd(), ".decantr", "evidence", "visual-manifest.json")
2496
+ ) ? ".decantr/evidence/visual-manifest.json" : null
2497
+ }
2498
+ };
2499
+ }
2254
2500
  case "decantr_get_execution_pack": {
2255
2501
  const contextDir = join2(process.cwd(), ".decantr", "context");
2256
2502
  const manifestPath = join2(contextDir, "pack-manifest.json");
@@ -2747,7 +2993,7 @@ function describeUpdate(operation, payload) {
2747
2993
  }
2748
2994
 
2749
2995
  // src/index.ts
2750
- var VERSION = "2.0.0";
2996
+ var VERSION = "2.2.0";
2751
2997
  var server = new Server({ name: "decantr", version: VERSION }, { capabilities: { tools: {} } });
2752
2998
  server.setRequestHandler(ListToolsRequestSchema, async () => {
2753
2999
  return { tools: TOOLS };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import "./chunk-FEXPLJKB.js";
1
+ import "./chunk-GIVGJE76.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/mcp-server",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "mcpName": "io.github.decantr-ai/mcp-server",
5
5
  "description": "MCP server for Decantr — exposes design intelligence, packs, and verification to AI coding assistants",
6
6
  "keywords": [
@@ -50,8 +50,8 @@
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/sdk": "^1.29.0",
52
52
  "@decantr/essence-spec": "2.0.1",
53
- "@decantr/verifier": "2.1.0",
54
- "@decantr/registry": "2.0.0"
53
+ "@decantr/registry": "2.2.0",
54
+ "@decantr/verifier": "2.2.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsup",