design-constraint-validator 2.1.0 → 2.2.1

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 (78) hide show
  1. package/README.md +17 -6
  2. package/cli/commands/build.d.ts.map +1 -1
  3. package/cli/commands/build.js +32 -24
  4. package/cli/commands/build.ts +26 -17
  5. package/cli/commands/graph.d.ts.map +1 -1
  6. package/cli/commands/graph.js +33 -16
  7. package/cli/commands/graph.ts +28 -15
  8. package/cli/commands/patch-apply.d.ts.map +1 -1
  9. package/cli/commands/patch-apply.js +4 -1
  10. package/cli/commands/patch-apply.ts +4 -1
  11. package/cli/commands/set.d.ts.map +1 -1
  12. package/cli/commands/set.js +18 -19
  13. package/cli/commands/set.ts +19 -19
  14. package/cli/commands/utils.d.ts +1 -0
  15. package/cli/commands/utils.d.ts.map +1 -1
  16. package/cli/commands/utils.js +20 -1
  17. package/cli/commands/utils.ts +23 -1
  18. package/cli/commands/validate.d.ts.map +1 -1
  19. package/cli/commands/validate.js +5 -17
  20. package/cli/commands/validate.ts +10 -18
  21. package/cli/commands/why.d.ts.map +1 -1
  22. package/cli/commands/why.js +22 -10
  23. package/cli/commands/why.ts +20 -9
  24. package/cli/config-schema.d.ts +144 -178
  25. package/cli/config-schema.d.ts.map +1 -1
  26. package/cli/config-schema.js +25 -5
  27. package/cli/config-schema.ts +27 -5
  28. package/cli/constraint-registry.d.ts.map +1 -1
  29. package/cli/constraint-registry.js +53 -15
  30. package/cli/constraint-registry.ts +53 -18
  31. package/cli/cross-axis-loader.d.ts +62 -0
  32. package/cli/cross-axis-loader.d.ts.map +1 -1
  33. package/cli/cross-axis-loader.js +186 -31
  34. package/cli/cross-axis-loader.ts +199 -24
  35. package/cli/dcv.js +23 -1
  36. package/cli/dcv.ts +23 -1
  37. package/cli/types.d.ts +19 -9
  38. package/cli/types.d.ts.map +1 -1
  39. package/cli/types.ts +23 -10
  40. package/cli/validate-api.d.ts.map +1 -1
  41. package/cli/validate-api.js +6 -1
  42. package/cli/validate-api.ts +6 -1
  43. package/core/constraints/cross-axis.d.ts.map +1 -1
  44. package/core/constraints/cross-axis.js +37 -9
  45. package/core/constraints/cross-axis.ts +37 -9
  46. package/core/constraints/monotonic.d.ts.map +1 -1
  47. package/core/constraints/monotonic.js +32 -8
  48. package/core/constraints/monotonic.ts +29 -8
  49. package/core/constraints/threshold.d.ts.map +1 -1
  50. package/core/constraints/threshold.js +24 -4
  51. package/core/constraints/threshold.ts +23 -4
  52. package/core/constraints/wcag.js +1 -1
  53. package/core/constraints/wcag.ts +1 -1
  54. package/core/flatten.d.ts.map +1 -1
  55. package/core/flatten.js +8 -0
  56. package/core/flatten.ts +9 -0
  57. package/core/poset.d.ts +6 -1
  58. package/core/poset.d.ts.map +1 -1
  59. package/core/poset.js +7 -2
  60. package/core/poset.ts +7 -2
  61. package/mcp/contracts.d.ts +1456 -13
  62. package/mcp/contracts.d.ts.map +1 -1
  63. package/mcp/contracts.js +45 -1
  64. package/mcp/contracts.ts +55 -1
  65. package/mcp/index.d.ts +6 -4
  66. package/mcp/index.d.ts.map +1 -1
  67. package/mcp/index.js +6 -3
  68. package/mcp/index.ts +28 -1
  69. package/mcp/insights.d.ts +94 -0
  70. package/mcp/insights.d.ts.map +1 -0
  71. package/mcp/insights.js +445 -0
  72. package/mcp/insights.ts +541 -0
  73. package/mcp/tools.d.ts +14 -3
  74. package/mcp/tools.d.ts.map +1 -1
  75. package/mcp/tools.js +133 -6
  76. package/mcp/tools.ts +188 -11
  77. package/package.json +2 -7
  78. package/server.json +3 -3
package/mcp/tools.js CHANGED
@@ -1,9 +1,14 @@
1
1
  import fs from 'node:fs';
2
2
  import { suggestIds } from '../core/cli-format.js';
3
3
  import { flattenTokens } from '../core/flatten.js';
4
- import { explain } from '../core/why.js';
4
+ import { explain as explainWhy } from '../core/why.js';
5
+ import { Engine } from '../core/engine.js';
6
+ import { ConstraintsSchema } from '../cli/config-schema.js';
7
+ import { loadConfig } from '../cli/config.js';
8
+ import { discoverConstraints } from '../cli/constraint-registry.js';
5
9
  import { validate } from '../cli/validate-api.js';
6
- import { graphInputShape, validateInputShape, whyInputShape } from './contracts.js';
10
+ import { describeConstraints, explain as explainInsight, suggestFix as suggestFixInsight, InsightError, } from './insights.js';
11
+ import { graphInputShape, validateInputShape, whyInputShape, listConstraintsInputShape, explainInputShape, suggestFixInputShape, } from './contracts.js';
7
12
  export class ToolExecutionError extends Error {
8
13
  code;
9
14
  details;
@@ -28,6 +33,17 @@ function toFailure(tool, error) {
28
33
  },
29
34
  };
30
35
  }
36
+ // Pure derivation errors (bad input, unsupported rule) carry their own code.
37
+ if (error instanceof InsightError) {
38
+ return {
39
+ ok: false,
40
+ tool,
41
+ error: {
42
+ code: error.code,
43
+ message: error.message,
44
+ },
45
+ };
46
+ }
31
47
  return {
32
48
  ok: false,
33
49
  tool,
@@ -73,9 +89,18 @@ function asJsonObject(value, label) {
73
89
  }
74
90
  throw new ToolExecutionError('invalid_input', `${label} must be a JSON object.`);
75
91
  }
92
+ function zodIssueMessages(error) {
93
+ return error.issues.map((issue) => {
94
+ const path = issue.path.length > 0 ? `.${issue.path.join('.')}` : '';
95
+ return `constraints${path}: ${issue.message}`;
96
+ });
97
+ }
76
98
  function resolveTokens(input) {
77
99
  if (input.tokens !== undefined) {
78
- return input.tokens;
100
+ // TASK-017: validate inline tokens at the handler boundary. A direct caller
101
+ // (not going through the MCP SDK's schema check) could otherwise pass an
102
+ // array/null/scalar that silently flattened to an empty, passing set.
103
+ return asJsonObject(input.tokens, 'tokens');
79
104
  }
80
105
  if (input.tokensPath !== undefined) {
81
106
  return asJsonObject(parseJsonFile(input.tokensPath), 'tokensPath contents');
@@ -83,7 +108,17 @@ function resolveTokens(input) {
83
108
  throw new ToolExecutionError('invalid_input', 'Provide either tokens or tokensPath.');
84
109
  }
85
110
  function constraints(input) {
86
- return input.constraints;
111
+ if (input.constraints === undefined)
112
+ return undefined;
113
+ // TASK-017: reject malformed inline constraints at the boundary.
114
+ const object = asJsonObject(input.constraints, 'constraints');
115
+ const parsed = ConstraintsSchema.safeParse(object);
116
+ if (!parsed.success) {
117
+ throw new ToolExecutionError('invalid_input', 'constraints must match DCV constraint config schema.', {
118
+ issues: zodIssueMessages(parsed.error),
119
+ });
120
+ }
121
+ return parsed.data;
87
122
  }
88
123
  export async function validateTool(input) {
89
124
  return executeTool('validate', () => {
@@ -91,7 +126,7 @@ export async function validateTool(input) {
91
126
  throw new ToolExecutionError('invalid_input', 'Provide either tokens or tokensPath.');
92
127
  }
93
128
  return validate({
94
- ...(input.tokens !== undefined ? { tokens: input.tokens } : {}),
129
+ ...(input.tokens !== undefined ? { tokens: asJsonObject(input.tokens, 'tokens') } : {}),
95
130
  ...(input.tokens === undefined && input.tokensPath !== undefined ? { tokensPath: input.tokensPath } : {}),
96
131
  ...(input.constraints !== undefined ? { constraints: constraints(input) } : {}),
97
132
  ...(input.configPath !== undefined ? { configPath: input.configPath } : {}),
@@ -111,7 +146,7 @@ export async function whyTool(input) {
111
146
  }
112
147
  return {
113
148
  ok: true,
114
- ...explain(input.tokenId, flat, edges),
149
+ ...explainWhy(input.tokenId, flat, edges),
115
150
  };
116
151
  });
117
152
  }
@@ -130,6 +165,80 @@ export async function graphTool(input) {
130
165
  };
131
166
  });
132
167
  }
168
+ /** Resolve the constraint config the same way validate() does: inline → configPath
169
+ * → discovered cwd config. */
170
+ function resolveConstraintsConfig(input) {
171
+ if (input.constraints !== undefined) {
172
+ return { constraints: constraints(input) };
173
+ }
174
+ const res = loadConfig(input.configPath);
175
+ if (!res.ok) {
176
+ throw new ToolExecutionError('invalid_config', res.error);
177
+ }
178
+ return res.value;
179
+ }
180
+ /** Flatten tokens, build an engine for value resolution, and discover the active
181
+ * constraint sources — the shared substrate for the read-only insight tools. */
182
+ function deriveContext(input) {
183
+ const tokens = resolveTokens(input);
184
+ const config = resolveConstraintsConfig(input);
185
+ const { flat, edges } = flattenTokens(tokens);
186
+ const init = {};
187
+ for (const t of Object.values(flat)) {
188
+ init[t.id] = t.value;
189
+ }
190
+ const engine = new Engine(init, edges);
191
+ const knownIds = new Set(Object.keys(init));
192
+ const sources = discoverConstraints({
193
+ config,
194
+ bp: input.breakpoint,
195
+ constraintsDir: input.constraintsDir ?? 'themes',
196
+ });
197
+ return {
198
+ descriptors: describeConstraints(sources),
199
+ // Token ids resolve to their value; anything else (a literal backdrop color)
200
+ // passes through, mirroring the WCAG plugin's resolveColor.
201
+ getValue: (idOrLiteral) => (knownIds.has(idOrLiteral) ? String(engine.get(idOrLiteral)) : idOrLiteral),
202
+ };
203
+ }
204
+ /** explain / suggest-fix accept a full violation OR loose ruleId + nodes. */
205
+ function resolveInsightTarget(input) {
206
+ if (input.violation) {
207
+ return {
208
+ ruleId: input.violation.ruleId,
209
+ nodes: input.violation.nodes ?? [],
210
+ context: input.violation.context,
211
+ };
212
+ }
213
+ if (input.ruleId) {
214
+ return {
215
+ ruleId: input.ruleId,
216
+ nodes: input.nodes ?? [],
217
+ context: input.context,
218
+ };
219
+ }
220
+ throw new ToolExecutionError('invalid_input', 'Provide either a violation object or ruleId (with nodes).');
221
+ }
222
+ export async function listConstraintsTool(input) {
223
+ return executeTool('list-constraints', () => {
224
+ const { descriptors } = deriveContext(input);
225
+ return { ok: true, constraints: descriptors, meta: { count: descriptors.length } };
226
+ });
227
+ }
228
+ export async function explainTool(input) {
229
+ return executeTool('explain', () => {
230
+ const { descriptors, getValue } = deriveContext(input);
231
+ const target = resolveInsightTarget(input);
232
+ return explainInsight({ ...target, getValue, descriptors });
233
+ });
234
+ }
235
+ export async function suggestFixTool(input) {
236
+ return executeTool('suggest-fix', () => {
237
+ const { descriptors, getValue } = deriveContext(input);
238
+ const target = resolveInsightTarget(input);
239
+ return suggestFixInsight({ ...target, getValue, descriptors, target: input.target });
240
+ });
241
+ }
133
242
  export const dcvMcpTools = [
134
243
  {
135
244
  name: 'validate',
@@ -149,6 +258,24 @@ export const dcvMcpTools = [
149
258
  inputSchema: graphInputShape,
150
259
  handler: graphTool,
151
260
  },
261
+ {
262
+ name: 'list-constraints',
263
+ description: 'List the active constraints (WCAG pairs, thresholds, order/lightness scales, cross-axis) for a token set/config. Read-only.',
264
+ inputSchema: listConstraintsInputShape,
265
+ handler: listConstraintsTool,
266
+ },
267
+ {
268
+ name: 'explain',
269
+ description: 'Explain a validation violation (WCAG, threshold, monotonic) in plain English plus machine-readable facts. Read-only.',
270
+ inputSchema: explainInputShape,
271
+ handler: explainTool,
272
+ },
273
+ {
274
+ name: 'suggest-fix',
275
+ description: 'Compute a verified satisfying value for a violation without writing it (WCAG color, threshold/monotonic boundary). Read-only.',
276
+ inputSchema: suggestFixInputShape,
277
+ handler: suggestFixTool,
278
+ },
152
279
  ];
153
280
  export function registerDcvMcpTools(server) {
154
281
  for (const tool of dcvMcpTools) {
package/mcp/tools.ts CHANGED
@@ -4,15 +4,44 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
4
4
  import type { z } from 'zod';
5
5
 
6
6
  import { suggestIds } from '../core/cli-format.js';
7
- import { flattenTokens, type TokenNode } from '../core/flatten.js';
8
- import { explain, type WhyReport } from '../core/why.js';
7
+ import { flattenTokens, type TokenNode, type FlatToken } from '../core/flatten.js';
8
+ import { explain as explainWhy, type WhyReport } from '../core/why.js';
9
+ import { Engine } from '../core/engine.js';
10
+ import { ConstraintsSchema } from '../cli/config-schema.js';
11
+ import { loadConfig } from '../cli/config.js';
12
+ import { discoverConstraints } from '../cli/constraint-registry.js';
9
13
  import { validate, type ValidateResult } from '../cli/validate-api.js';
10
14
  import type { DcvConfig } from '../cli/types.js';
11
15
  import type { Breakpoint } from '../core/breakpoints.js';
12
- import type { JsonObject, ValidateToolInput, WhyToolInput, GraphToolInput } from './contracts.js';
13
- import { graphInputShape, validateInputShape, whyInputShape } from './contracts.js';
14
-
15
- export type DcvMcpToolName = 'validate' | 'why' | 'graph';
16
+ import {
17
+ describeConstraints,
18
+ explain as explainInsight,
19
+ suggestFix as suggestFixInsight,
20
+ InsightError,
21
+ type ConstraintDescriptor,
22
+ type ExplainResult,
23
+ type SuggestResult,
24
+ type ValueResolver,
25
+ } from './insights.js';
26
+ import type {
27
+ JsonObject,
28
+ ValidateToolInput,
29
+ WhyToolInput,
30
+ GraphToolInput,
31
+ ListConstraintsToolInput,
32
+ ExplainToolInput,
33
+ SuggestFixToolInput,
34
+ } from './contracts.js';
35
+ import {
36
+ graphInputShape,
37
+ validateInputShape,
38
+ whyInputShape,
39
+ listConstraintsInputShape,
40
+ explainInputShape,
41
+ suggestFixInputShape,
42
+ } from './contracts.js';
43
+
44
+ export type DcvMcpToolName = 'validate' | 'why' | 'graph' | 'list-constraints' | 'explain' | 'suggest-fix';
16
45
 
17
46
  export interface ToolFailure {
18
47
  ok: false;
@@ -52,7 +81,7 @@ export type WhyToolResult = { ok: true } & WhyReport;
52
81
  interface TokenInput {
53
82
  tokens?: JsonObject;
54
83
  tokensPath?: string;
55
- constraints?: JsonObject;
84
+ constraints?: unknown;
56
85
  configPath?: string;
57
86
  constraintsDir?: string;
58
87
  breakpoint?: Breakpoint;
@@ -82,6 +111,18 @@ function toFailure(tool: DcvMcpToolName, error: unknown): ToolFailure {
82
111
  };
83
112
  }
84
113
 
114
+ // Pure derivation errors (bad input, unsupported rule) carry their own code.
115
+ if (error instanceof InsightError) {
116
+ return {
117
+ ok: false,
118
+ tool,
119
+ error: {
120
+ code: error.code,
121
+ message: error.message,
122
+ },
123
+ };
124
+ }
125
+
85
126
  return {
86
127
  ok: false,
87
128
  tool,
@@ -136,9 +177,19 @@ function asJsonObject(value: unknown, label: string): JsonObject {
136
177
  throw new ToolExecutionError('invalid_input', `${label} must be a JSON object.`);
137
178
  }
138
179
 
180
+ function zodIssueMessages(error: z.ZodError): string[] {
181
+ return error.issues.map((issue) => {
182
+ const path = issue.path.length > 0 ? `.${issue.path.join('.')}` : '';
183
+ return `constraints${path}: ${issue.message}`;
184
+ });
185
+ }
186
+
139
187
  function resolveTokens(input: TokenInput): TokenNode {
140
188
  if (input.tokens !== undefined) {
141
- return input.tokens as unknown as TokenNode;
189
+ // TASK-017: validate inline tokens at the handler boundary. A direct caller
190
+ // (not going through the MCP SDK's schema check) could otherwise pass an
191
+ // array/null/scalar that silently flattened to an empty, passing set.
192
+ return asJsonObject(input.tokens, 'tokens') as unknown as TokenNode;
142
193
  }
143
194
 
144
195
  if (input.tokensPath !== undefined) {
@@ -149,7 +200,16 @@ function resolveTokens(input: TokenInput): TokenNode {
149
200
  }
150
201
 
151
202
  function constraints(input: TokenInput): DcvConfig['constraints'] | undefined {
152
- return input.constraints as DcvConfig['constraints'] | undefined;
203
+ if (input.constraints === undefined) return undefined;
204
+ // TASK-017: reject malformed inline constraints at the boundary.
205
+ const object = asJsonObject(input.constraints, 'constraints');
206
+ const parsed = ConstraintsSchema.safeParse(object);
207
+ if (!parsed.success) {
208
+ throw new ToolExecutionError('invalid_input', 'constraints must match DCV constraint config schema.', {
209
+ issues: zodIssueMessages(parsed.error),
210
+ });
211
+ }
212
+ return parsed.data;
153
213
  }
154
214
 
155
215
  export async function validateTool(input: ValidateToolInput): Promise<ToolResponse<ValidateResult>> {
@@ -159,7 +219,7 @@ export async function validateTool(input: ValidateToolInput): Promise<ToolRespon
159
219
  }
160
220
 
161
221
  return validate({
162
- ...(input.tokens !== undefined ? { tokens: input.tokens as unknown as TokenNode } : {}),
222
+ ...(input.tokens !== undefined ? { tokens: asJsonObject(input.tokens, 'tokens') as unknown as TokenNode } : {}),
163
223
  ...(input.tokens === undefined && input.tokensPath !== undefined ? { tokensPath: input.tokensPath } : {}),
164
224
  ...(input.constraints !== undefined ? { constraints: constraints(input) } : {}),
165
225
  ...(input.configPath !== undefined ? { configPath: input.configPath } : {}),
@@ -181,7 +241,7 @@ export async function whyTool(input: WhyToolInput): Promise<ToolResponse<WhyTool
181
241
 
182
242
  return {
183
243
  ok: true as const,
184
- ...explain(input.tokenId, flat, edges),
244
+ ...explainWhy(input.tokenId, flat, edges),
185
245
  };
186
246
  });
187
247
  }
@@ -203,10 +263,109 @@ export async function graphTool(input: GraphToolInput): Promise<ToolResponse<Gra
203
263
  });
204
264
  }
205
265
 
266
+ export interface ListConstraintsResult {
267
+ ok: true;
268
+ constraints: ConstraintDescriptor[];
269
+ meta: { count: number };
270
+ }
271
+
272
+ /** Resolve the constraint config the same way validate() does: inline → configPath
273
+ * → discovered cwd config. */
274
+ function resolveConstraintsConfig(input: TokenInput): DcvConfig {
275
+ if (input.constraints !== undefined) {
276
+ return { constraints: constraints(input) };
277
+ }
278
+ const res = loadConfig(input.configPath);
279
+ if (!res.ok) {
280
+ throw new ToolExecutionError('invalid_config', res.error);
281
+ }
282
+ return res.value;
283
+ }
284
+
285
+ interface DerivedContext {
286
+ descriptors: ConstraintDescriptor[];
287
+ getValue: ValueResolver;
288
+ }
289
+
290
+ /** Flatten tokens, build an engine for value resolution, and discover the active
291
+ * constraint sources — the shared substrate for the read-only insight tools. */
292
+ function deriveContext(input: TokenInput): DerivedContext {
293
+ const tokens = resolveTokens(input);
294
+ const config = resolveConstraintsConfig(input);
295
+
296
+ const { flat, edges } = flattenTokens(tokens);
297
+ const init: Record<string, string | number> = {};
298
+ for (const t of Object.values(flat)) {
299
+ init[(t as FlatToken).id] = (t as FlatToken).value;
300
+ }
301
+ const engine = new Engine(init, edges);
302
+ const knownIds = new Set(Object.keys(init));
303
+
304
+ const sources = discoverConstraints({
305
+ config,
306
+ bp: input.breakpoint,
307
+ constraintsDir: input.constraintsDir ?? 'themes',
308
+ });
309
+
310
+ return {
311
+ descriptors: describeConstraints(sources),
312
+ // Token ids resolve to their value; anything else (a literal backdrop color)
313
+ // passes through, mirroring the WCAG plugin's resolveColor.
314
+ getValue: (idOrLiteral) => (knownIds.has(idOrLiteral) ? String(engine.get(idOrLiteral)) : idOrLiteral),
315
+ };
316
+ }
317
+
318
+ /** explain / suggest-fix accept a full violation OR loose ruleId + nodes. */
319
+ function resolveInsightTarget(
320
+ input: ExplainToolInput | SuggestFixToolInput,
321
+ ): { ruleId: string; nodes: string[]; context?: Record<string, unknown> } {
322
+ if (input.violation) {
323
+ return {
324
+ ruleId: input.violation.ruleId,
325
+ nodes: input.violation.nodes ?? [],
326
+ context: input.violation.context as Record<string, unknown> | undefined,
327
+ };
328
+ }
329
+ if (input.ruleId) {
330
+ return {
331
+ ruleId: input.ruleId,
332
+ nodes: input.nodes ?? [],
333
+ context: input.context as Record<string, unknown> | undefined,
334
+ };
335
+ }
336
+ throw new ToolExecutionError('invalid_input', 'Provide either a violation object or ruleId (with nodes).');
337
+ }
338
+
339
+ export async function listConstraintsTool(input: ListConstraintsToolInput): Promise<ToolResponse<ListConstraintsResult>> {
340
+ return executeTool('list-constraints', () => {
341
+ const { descriptors } = deriveContext(input);
342
+ return { ok: true as const, constraints: descriptors, meta: { count: descriptors.length } };
343
+ });
344
+ }
345
+
346
+ export async function explainTool(input: ExplainToolInput): Promise<ToolResponse<ExplainResult>> {
347
+ return executeTool('explain', () => {
348
+ const { descriptors, getValue } = deriveContext(input);
349
+ const target = resolveInsightTarget(input);
350
+ return explainInsight({ ...target, getValue, descriptors });
351
+ });
352
+ }
353
+
354
+ export async function suggestFixTool(input: SuggestFixToolInput): Promise<ToolResponse<SuggestResult>> {
355
+ return executeTool('suggest-fix', () => {
356
+ const { descriptors, getValue } = deriveContext(input);
357
+ const target = resolveInsightTarget(input);
358
+ return suggestFixInsight({ ...target, getValue, descriptors, target: input.target });
359
+ });
360
+ }
361
+
206
362
  export const dcvMcpTools: Array<
207
363
  | ToolDefinition<ValidateToolInput, ValidateResult>
208
364
  | ToolDefinition<WhyToolInput, WhyToolResult>
209
365
  | ToolDefinition<GraphToolInput, GraphToolResult>
366
+ | ToolDefinition<ListConstraintsToolInput, ListConstraintsResult>
367
+ | ToolDefinition<ExplainToolInput, ExplainResult>
368
+ | ToolDefinition<SuggestFixToolInput, SuggestResult>
210
369
  > = [
211
370
  {
212
371
  name: 'validate',
@@ -226,6 +385,24 @@ export const dcvMcpTools: Array<
226
385
  inputSchema: graphInputShape,
227
386
  handler: graphTool,
228
387
  },
388
+ {
389
+ name: 'list-constraints',
390
+ description: 'List the active constraints (WCAG pairs, thresholds, order/lightness scales, cross-axis) for a token set/config. Read-only.',
391
+ inputSchema: listConstraintsInputShape,
392
+ handler: listConstraintsTool,
393
+ },
394
+ {
395
+ name: 'explain',
396
+ description: 'Explain a validation violation (WCAG, threshold, monotonic) in plain English plus machine-readable facts. Read-only.',
397
+ inputSchema: explainInputShape,
398
+ handler: explainTool,
399
+ },
400
+ {
401
+ name: 'suggest-fix',
402
+ description: 'Compute a verified satisfying value for a violation without writing it (WCAG color, threshold/monotonic boundary). Read-only.',
403
+ inputSchema: suggestFixInputShape,
404
+ handler: suggestFixTool,
405
+ },
229
406
  ];
230
407
 
231
408
  export function registerDcvMcpTools(server: McpServer): void {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "design-constraint-validator",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "Mathematical constraint validator for design systems — ensuring consistency, accessibility, and logical coherence",
5
5
  "type": "module",
6
6
  "engines": {
@@ -26,16 +26,11 @@
26
26
  "design-constraint-validator": "./cli/index.js",
27
27
  "dcv-mcp": "./mcp/index.js"
28
28
  },
29
- "mcpName": "io.github.cseperkepapp/design-constraint-validator",
29
+ "mcpName": "io.github.CseperkePapp/design-constraint-validator",
30
30
  "scripts": {
31
31
  "test": "vitest run --exclude \"**/*.test.js\"",
32
32
  "test:watch": "vitest --exclude \"**/*.test.js\"",
33
33
  "typecheck": "tsc --noEmit",
34
- "workflow:typecheck": "tsc -p tsconfig.workflow-automation.json --noEmit",
35
- "validate-headers": "tsx scripts/validate-headers.ts",
36
- "workflow:test": "vitest run src/__tests__/task-workflow-integrity.test.ts src/__tests__/sync-task-index.test.ts",
37
- "task:sync": "tsx scripts/sync-task-index.ts",
38
- "rename-done-tasks": "tsx scripts/rename-done-tasks.ts",
39
34
  "build": "tsc",
40
35
  "lint": "eslint . --ext .js,.ts,.mjs,.cjs",
41
36
  "format": "prettier -w .",
package/server.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
- "name": "io.github.cseperkepapp/design-constraint-validator",
3
+ "name": "io.github.CseperkePapp/design-constraint-validator",
4
4
  "title": "Design Constraint Validator",
5
5
  "description": "Validate design tokens for accessibility, scales, and design-system constraint consistency.",
6
- "version": "2.1.0",
6
+ "version": "2.2.1",
7
7
  "repository": {
8
8
  "url": "https://github.com/CseperkePapp/design-constraint-validator",
9
9
  "source": "github"
@@ -12,7 +12,7 @@
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "design-constraint-validator",
15
- "version": "2.1.0",
15
+ "version": "2.2.1",
16
16
  "transport": {
17
17
  "type": "stdio"
18
18
  }