@stackwright-pro/mcp 0.2.0-alpha.80 → 0.2.0-alpha.82

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/server.js CHANGED
@@ -1613,24 +1613,28 @@ var import_path3 = require("path");
1613
1613
  var OTTER_NAME_TO_PHASE = [
1614
1614
  ["designer", "designer"],
1615
1615
  ["theme", "theme"],
1616
+ ["scaffold", "scaffold"],
1616
1617
  ["api", "api"],
1617
1618
  ["auth", "auth"],
1618
1619
  ["dashboard", "dashboard"],
1619
1620
  ["data", "data"],
1620
1621
  ["page", "pages"],
1621
1622
  ["workflow", "workflow"],
1623
+ ["services", "services"],
1622
1624
  ["polish", "polish"],
1623
1625
  ["geo", "geo"]
1624
1626
  ];
1625
1627
  var PHASE_TO_OTTER = {
1626
1628
  designer: "stackwright-pro-designer-otter",
1627
1629
  theme: "stackwright-pro-theme-otter",
1630
+ scaffold: "stackwright-pro-scaffold-otter",
1628
1631
  api: "stackwright-pro-api-otter",
1629
1632
  auth: "stackwright-pro-auth-otter",
1630
1633
  pages: "stackwright-pro-page-otter",
1631
1634
  dashboard: "stackwright-pro-dashboard-otter",
1632
1635
  data: "stackwright-pro-data-otter",
1633
1636
  workflow: "stackwright-pro-workflow-otter",
1637
+ services: "stackwright-services-otter",
1634
1638
  polish: "stackwright-pro-polish-otter",
1635
1639
  geo: "stackwright-pro-geo-otter"
1636
1640
  };
@@ -2015,7 +2019,7 @@ function registerOrchestrationTools(server2) {
2015
2019
  }
2016
2020
 
2017
2021
  // src/tools/pipeline.ts
2018
- var import_zod11 = require("zod");
2022
+ var import_zod13 = require("zod");
2019
2023
  var import_fs5 = require("fs");
2020
2024
  var import_path5 = require("path");
2021
2025
  var import_crypto3 = require("crypto");
@@ -2343,7 +2347,343 @@ function registerArtifactSigningTools(server2) {
2343
2347
  }
2344
2348
 
2345
2349
  // src/tools/pipeline.ts
2350
+ var import_types3 = require("@stackwright-pro/types");
2351
+
2352
+ // src/tools/get-schema.ts
2353
+ var import_zod12 = require("zod");
2354
+ var import_types2 = require("@stackwright-pro/types");
2355
+
2356
+ // src/tools/validate-yaml-fragment.ts
2357
+ var import_zod11 = require("zod");
2358
+ var import_js_yaml = require("js-yaml");
2346
2359
  var import_types = require("@stackwright-pro/types");
2360
+ var SUPPORTED_SCHEMA_NAMES = [
2361
+ "workflow_step",
2362
+ "workflow_definition",
2363
+ "workflow_field",
2364
+ "workflow_action",
2365
+ "navigation_item",
2366
+ "auth_config"
2367
+ ];
2368
+ var NavigationLinkSchema = import_zod11.z.lazy(
2369
+ () => import_zod11.z.object({
2370
+ label: import_zod11.z.string().min(1),
2371
+ href: import_zod11.z.string().min(1),
2372
+ children: import_zod11.z.array(NavigationLinkSchema).optional()
2373
+ })
2374
+ );
2375
+ var NavigationSectionSchema = import_zod11.z.object({
2376
+ section: import_zod11.z.string().min(1),
2377
+ items: import_zod11.z.array(NavigationLinkSchema)
2378
+ });
2379
+ var NavigationItemSchema = import_zod11.z.union([
2380
+ NavigationLinkSchema,
2381
+ NavigationSectionSchema
2382
+ ]);
2383
+ var FIELD_SYNONYMS = {
2384
+ workflow_step: {
2385
+ title: "label",
2386
+ name: "label (at step level, use label: not name:)",
2387
+ description: "message (use message: for step-level descriptive text)",
2388
+ style: 'theme.variant \u2014 use theme: { variant: "primary" } not style: "primary"',
2389
+ transitions: 'on_submit.transition \u2014 use on_submit: { transition: "next_step" } not transitions: [{then:...}]'
2390
+ },
2391
+ workflow_definition: {
2392
+ title: "label",
2393
+ name: "label (workflow-level label, not name:)",
2394
+ description: "description is valid here \u2014 but message: is not (that is a step field)",
2395
+ type: "id (workflows are identified by id:, not type:)"
2396
+ },
2397
+ workflow_field: {
2398
+ id: "name (field-level identifier is name:, NOT id:)",
2399
+ title: "label (field display text is label:, NOT title:)",
2400
+ description: "placeholder or label (no description field on WorkflowField)"
2401
+ },
2402
+ workflow_action: {
2403
+ style: 'theme.variant \u2014 use theme: { variant: "primary" } not style: "primary"',
2404
+ then: "transition (action-level next-step is transition:, NOT then:)",
2405
+ on_click: 'action: "service:..." (extract the service reference from on_click.action)',
2406
+ title: "label (action display text is label:, NOT title:)",
2407
+ name: "label (action display text is label:, NOT name:)"
2408
+ },
2409
+ navigation_item: {
2410
+ children: "items (NavigationSection uses items: not children: \u2014 also use section: instead of label: for section headers)",
2411
+ label: 'section (if this is a nav group/section, use section: "Title" and items: [...], not label:)',
2412
+ title: "section (NavigationSection header field is section:, NOT title:)"
2413
+ },
2414
+ auth_config: {
2415
+ method: 'type \u2014 auth config discriminates on type: "oidc" | "pki", NOT method:',
2416
+ provider_type: 'type \u2014 use type: "oidc" or type: "pki"',
2417
+ strategy: "type \u2014 top-level discriminator is type:, not strategy:"
2418
+ }
2419
+ };
2420
+ function getSchema(name) {
2421
+ switch (name) {
2422
+ case "workflow_step":
2423
+ return import_types.WorkflowStepSchema;
2424
+ case "workflow_definition":
2425
+ return import_types.WorkflowDefinitionSchema;
2426
+ case "workflow_field":
2427
+ return import_types.WorkflowFieldSchema;
2428
+ case "workflow_action":
2429
+ return import_types.WorkflowActionSchema;
2430
+ case "navigation_item":
2431
+ return NavigationItemSchema;
2432
+ case "auth_config":
2433
+ return import_types.authConfigSchema;
2434
+ }
2435
+ }
2436
+ function enrichErrors(issues, synonyms) {
2437
+ return issues.map((issue) => {
2438
+ const pathStr = issue.path.join(".");
2439
+ const lastSegment = issue.path[issue.path.length - 1];
2440
+ const fieldName = typeof lastSegment === "string" ? lastSegment : void 0;
2441
+ const didYouMean = fieldName ? synonyms[fieldName] : void 0;
2442
+ return {
2443
+ path: pathStr || "(root)",
2444
+ message: issue.message,
2445
+ ...didYouMean ? { didYouMean } : {}
2446
+ };
2447
+ });
2448
+ }
2449
+ function handleValidateYamlFragment(input) {
2450
+ const { schemaName, yaml } = input;
2451
+ if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
2452
+ return {
2453
+ valid: false,
2454
+ parseError: `Unknown schemaName: "${schemaName}". Supported: ${SUPPORTED_SCHEMA_NAMES.join(", ")}`
2455
+ };
2456
+ }
2457
+ const name = schemaName;
2458
+ let parsed;
2459
+ try {
2460
+ parsed = (0, import_js_yaml.load)(yaml);
2461
+ } catch (err) {
2462
+ return {
2463
+ valid: false,
2464
+ parseError: `YAML parse error: ${err instanceof Error ? err.message : String(err)}`
2465
+ };
2466
+ }
2467
+ const schema = getSchema(name);
2468
+ const result = schema.safeParse(parsed);
2469
+ if (result.success) {
2470
+ return { valid: true };
2471
+ }
2472
+ const synonyms = FIELD_SYNONYMS[name];
2473
+ const errors = enrichErrors(result.error.issues, synonyms);
2474
+ return { valid: false, errors };
2475
+ }
2476
+ function registerValidateYamlFragmentTool(server2) {
2477
+ server2.tool(
2478
+ "stackwright_pro_validate_yaml_fragment",
2479
+ 'Validates a YAML snippet against a canonical Zod schema before writing to disk. Returns actionable errors with "did you mean?" suggestions for common field-name drift (title vs label, id vs name, style vs theme.variant, etc.). Call this before stackwright_pro_safe_write to catch schema violations in-session.',
2480
+ {
2481
+ schemaName: import_zod11.z.enum(SUPPORTED_SCHEMA_NAMES).describe(
2482
+ "Schema to validate against. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
2483
+ ),
2484
+ yaml: import_zod11.z.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
2485
+ },
2486
+ async ({ schemaName, yaml }) => {
2487
+ const result = handleValidateYamlFragment({ schemaName, yaml });
2488
+ return {
2489
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2490
+ };
2491
+ }
2492
+ );
2493
+ }
2494
+
2495
+ // src/tools/get-schema.ts
2496
+ var NavigationLinkSchema2 = import_zod12.z.lazy(
2497
+ () => import_zod12.z.object({
2498
+ label: import_zod12.z.string().min(1),
2499
+ href: import_zod12.z.string().min(1),
2500
+ children: import_zod12.z.array(NavigationLinkSchema2).optional()
2501
+ })
2502
+ );
2503
+ var NavigationSectionSchema2 = import_zod12.z.object({
2504
+ section: import_zod12.z.string().min(1),
2505
+ items: import_zod12.z.array(NavigationLinkSchema2)
2506
+ });
2507
+ var NavigationItemSchema2 = import_zod12.z.union([
2508
+ NavigationLinkSchema2,
2509
+ NavigationSectionSchema2
2510
+ ]);
2511
+ function getZodSchema(name) {
2512
+ switch (name) {
2513
+ case "workflow_step":
2514
+ return import_types2.WorkflowStepSchema;
2515
+ case "workflow_definition":
2516
+ return import_types2.WorkflowDefinitionSchema;
2517
+ case "workflow_field":
2518
+ return import_types2.WorkflowFieldSchema;
2519
+ case "workflow_action":
2520
+ return import_types2.WorkflowActionSchema;
2521
+ case "navigation_item":
2522
+ return NavigationItemSchema2;
2523
+ case "auth_config":
2524
+ return import_types2.authConfigSchema;
2525
+ }
2526
+ }
2527
+ function formatType(node, depth = 0) {
2528
+ if (node.const !== void 0) return JSON.stringify(node.const);
2529
+ if (node.enum) return node.enum.map((v) => JSON.stringify(v)).join(" | ");
2530
+ if (node.oneOf || node.anyOf) {
2531
+ const branches = node.oneOf ?? node.anyOf ?? [];
2532
+ return branches.map((b) => {
2533
+ const disc = b.properties ? Object.entries(b.properties).filter(([, v]) => v.const !== void 0).map(([k, v]) => `${k}: ${JSON.stringify(v.const)}`).join(", ") : "";
2534
+ return disc ? `{ ${disc}, ... }` : "{ ... }";
2535
+ }).join(" | ");
2536
+ }
2537
+ if (node.type === "array" && node.items) {
2538
+ return `${formatType(node.items, depth)}[]`;
2539
+ }
2540
+ if (node.type === "object" && node.properties && depth < 2) {
2541
+ const inner = Object.entries(node.properties).map(([k, v]) => `${k}: ${formatType(v, depth + 1)}`).join(", ");
2542
+ return `{ ${inner} }`;
2543
+ }
2544
+ const base = Array.isArray(node.type) ? node.type.join(" | ") : node.type ?? "unknown";
2545
+ const constraints = [];
2546
+ if (node.maxLength !== void 0) constraints.push(`max ${node.maxLength} chars`);
2547
+ if (node.minLength !== void 0) constraints.push(`min ${node.minLength} chars`);
2548
+ if (node.pattern) constraints.push(`pattern: ${node.pattern}`);
2549
+ if (node.minimum !== void 0) constraints.push(`min ${node.minimum}`);
2550
+ if (node.maximum !== void 0) constraints.push(`max ${node.maximum}`);
2551
+ return constraints.length > 0 ? `${base} (${constraints.join(", ")})` : base;
2552
+ }
2553
+ function formatObjectSchema(node, schemaName, synonyms) {
2554
+ const lines = [];
2555
+ const displayName = schemaName.split("_").map((w) => w[0].toUpperCase() + w.slice(1)).join("");
2556
+ lines.push(`${displayName}:`);
2557
+ if (node.oneOf || node.anyOf) {
2558
+ const branches = node.oneOf ?? node.anyOf ?? [];
2559
+ lines.push(" Discriminated union \u2014 choose one variant:");
2560
+ for (const branch of branches) {
2561
+ const req = new Set(branch.required ?? []);
2562
+ const disc = Object.entries(branch.properties ?? {}).filter(([, v]) => v.const !== void 0).map(([k, v]) => `${k}: ${JSON.stringify(v.const)}`).join(", ");
2563
+ lines.push(` VARIANT (${disc}):`);
2564
+ for (const [field, fieldNode] of Object.entries(branch.properties ?? {})) {
2565
+ if (fieldNode.const !== void 0) continue;
2566
+ const isRequired = req.has(field);
2567
+ const hint = synonyms[field] ? ` -- WARNING: use ${field}: not ${synonyms[field]}` : "";
2568
+ lines.push(
2569
+ ` ${isRequired ? "REQUIRED" : "optional"}: ${field}: ${formatType(fieldNode)}${hint}`
2570
+ );
2571
+ }
2572
+ }
2573
+ return lines.join("\n");
2574
+ }
2575
+ const required = new Set(node.required ?? []);
2576
+ const properties = node.properties ?? {};
2577
+ const requiredFields = Object.entries(properties).filter(([k]) => required.has(k));
2578
+ const optionalFields = Object.entries(properties).filter(([k]) => !required.has(k));
2579
+ if (requiredFields.length > 0) {
2580
+ lines.push(" REQUIRED:");
2581
+ for (const [field, fieldNode] of requiredFields) {
2582
+ const typeStr = formatType(fieldNode);
2583
+ const warn = synonyms[field] ? ` -- CAUTION: this schema uses ${field}` : "";
2584
+ lines.push(` ${field}: ${typeStr}${warn}`);
2585
+ }
2586
+ }
2587
+ if (optionalFields.length > 0) {
2588
+ lines.push(" OPTIONAL:");
2589
+ for (const [field, fieldNode] of optionalFields) {
2590
+ const typeStr = formatType(fieldNode);
2591
+ lines.push(` ${field}: ${typeStr}`);
2592
+ }
2593
+ }
2594
+ return lines.join("\n");
2595
+ }
2596
+ function handleGetSchema(input) {
2597
+ const { schemaName } = input;
2598
+ if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
2599
+ return {
2600
+ error: `Unknown schemaName: "${schemaName}". Supported: ${SUPPORTED_SCHEMA_NAMES.join(", ")}`
2601
+ };
2602
+ }
2603
+ const name = schemaName;
2604
+ const schema = getZodSchema(name);
2605
+ const synonyms = FIELD_SYNONYMS[name];
2606
+ let jsonSchema;
2607
+ try {
2608
+ const raw = import_zod12.z.toJSONSchema(schema, { unrepresentable: "any" });
2609
+ jsonSchema = raw;
2610
+ } catch {
2611
+ jsonSchema = { type: "object", description: "Schema introspection unavailable for this type" };
2612
+ }
2613
+ const summary = formatObjectSchema(jsonSchema, name, synonyms);
2614
+ const synonymWarnings = Object.entries(synonyms).map(
2615
+ ([wrong, correct]) => ` WARNING: use "${correct}" \u2014 NOT "${wrong}"`
2616
+ );
2617
+ const fullText = [
2618
+ summary,
2619
+ "",
2620
+ "FIELD NAME WARNINGS (common drift patterns):",
2621
+ ...synonymWarnings
2622
+ ].join("\n");
2623
+ const tokenEstimate = Math.ceil(fullText.length / 4);
2624
+ return {
2625
+ schemaName: name,
2626
+ summary,
2627
+ synonymWarnings,
2628
+ tokenEstimate
2629
+ };
2630
+ }
2631
+ var PHASE_SCHEMA_NAMES = {
2632
+ workflow: ["workflow_step", "workflow_definition", "workflow_field", "workflow_action"],
2633
+ pages: ["navigation_item"],
2634
+ dashboard: ["navigation_item"],
2635
+ polish: ["navigation_item"],
2636
+ auth: ["auth_config"]
2637
+ };
2638
+ function buildSchemaReferenceBlock(phase) {
2639
+ const schemaNames = PHASE_SCHEMA_NAMES[phase];
2640
+ if (!schemaNames || schemaNames.length === 0) return null;
2641
+ const sections = [
2642
+ "CANONICAL_SCHEMA_REFERENCE (authoritative \u2014 use exact field names below):",
2643
+ ""
2644
+ ];
2645
+ for (const name of schemaNames) {
2646
+ const result = handleGetSchema({ schemaName: name });
2647
+ if ("error" in result) continue;
2648
+ sections.push(result.summary);
2649
+ if (result.synonymWarnings.length > 0) {
2650
+ sections.push(" Common mistakes to avoid:");
2651
+ sections.push(...result.synonymWarnings);
2652
+ }
2653
+ sections.push("");
2654
+ }
2655
+ return sections.join("\n");
2656
+ }
2657
+ function registerGetSchemaTool(server2) {
2658
+ server2.tool(
2659
+ "stackwright_pro_get_schema",
2660
+ 'Returns a compact, LLM-friendly summary of a canonical schema derived programmatically from Zod. Includes required/optional fields, types, constraints, and "did you mean?" warnings for common field-name drift. Use before authoring YAML to confirm exact field names.',
2661
+ {
2662
+ schemaName: import_zod12.z.enum(SUPPORTED_SCHEMA_NAMES).describe(
2663
+ "Schema to summarize. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
2664
+ )
2665
+ },
2666
+ async ({ schemaName }) => {
2667
+ const result = handleGetSchema({ schemaName });
2668
+ if ("error" in result) {
2669
+ return {
2670
+ content: [{ type: "text", text: JSON.stringify({ error: result.error }) }],
2671
+ isError: true
2672
+ };
2673
+ }
2674
+ return {
2675
+ content: [
2676
+ {
2677
+ type: "text",
2678
+ text: JSON.stringify(result, null, 2)
2679
+ }
2680
+ ]
2681
+ };
2682
+ }
2683
+ );
2684
+ }
2685
+
2686
+ // src/tools/pipeline.ts
2347
2687
  var PHASE_ORDER = [
2348
2688
  "designer",
2349
2689
  "theme",
@@ -2911,6 +3251,10 @@ ${JSON.stringify(content, null, 2)}`
2911
3251
  const artifactSchema = PHASE_ARTIFACT_SCHEMA[phase];
2912
3252
  parts.push("", "REQUIRED_ARTIFACT_SCHEMA:");
2913
3253
  parts.push(artifactSchema);
3254
+ const schemaRef = buildSchemaReferenceBlock(phase);
3255
+ if (schemaRef) {
3256
+ parts.push("", schemaRef);
3257
+ }
2914
3258
  parts.push("", "Execute using these answers and the upstream artifacts provided.");
2915
3259
  const prompt = parts.join("\n");
2916
3260
  const dependenciesSatisfied = missingDependencies.length === 0;
@@ -2945,6 +3289,7 @@ var OFF_SCRIPT_PATTERNS = [
2945
3289
  var PHASE_REQUIRED_KEYS = {
2946
3290
  designer: ["designLanguage", "themeTokenSeeds"],
2947
3291
  theme: ["tokens"],
3292
+ scaffold: ["version", "generatedBy", "appRouterFiles"],
2948
3293
  api: ["entities", "version", "generatedBy"],
2949
3294
  auth: ["version", "generatedBy"],
2950
3295
  data: ["version", "generatedBy", "strategy", "collections"],
@@ -3285,7 +3630,7 @@ function handleValidateArtifact(input) {
3285
3630
  workflow: (artifact2) => {
3286
3631
  const workflowConfig = artifact2["workflowConfig"];
3287
3632
  if (!workflowConfig) return { success: true };
3288
- const result = import_types.WorkflowFileSchema.safeParse(workflowConfig);
3633
+ const result = import_types3.WorkflowFileSchema.safeParse(workflowConfig);
3289
3634
  if (!result.success) {
3290
3635
  const issues = result.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
3291
3636
  return { success: false, error: { message: issues } };
@@ -3295,7 +3640,7 @@ function handleValidateArtifact(input) {
3295
3640
  auth: (artifact2) => {
3296
3641
  const authConfig = artifact2["authConfig"];
3297
3642
  if (!authConfig) return { success: true };
3298
- const result = import_types.authConfigSchema.safeParse(authConfig);
3643
+ const result = import_types3.authConfigSchema.safeParse(authConfig);
3299
3644
  if (!result.success) {
3300
3645
  const issues = result.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
3301
3646
  return { success: false, error: { message: issues } };
@@ -3367,21 +3712,21 @@ function registerPipelineTools(server2) {
3367
3712
  "stackwright_pro_set_pipeline_state",
3368
3713
  `Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
3369
3714
  {
3370
- phase: import_zod11.z.string().optional().describe('Phase to update, e.g. "designer"'),
3371
- field: import_zod11.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
3372
- value: boolCoerce(import_zod11.z.boolean().optional()).describe(
3715
+ phase: import_zod13.z.string().optional().describe('Phase to update, e.g. "designer"'),
3716
+ field: import_zod13.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
3717
+ value: boolCoerce(import_zod13.z.boolean().optional()).describe(
3373
3718
  'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
3374
3719
  ),
3375
- status: import_zod11.z.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level status override"),
3376
- incrementRetry: boolCoerce(import_zod11.z.boolean().optional()).describe(
3720
+ status: import_zod13.z.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level status override"),
3721
+ incrementRetry: boolCoerce(import_zod13.z.boolean().optional()).describe(
3377
3722
  "Bump retryCount by 1 \u2014 must be a JSON boolean"
3378
3723
  ),
3379
3724
  updates: jsonCoerce(
3380
- import_zod11.z.array(
3381
- import_zod11.z.object({
3382
- phase: import_zod11.z.string(),
3383
- field: import_zod11.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
3384
- value: import_zod11.z.boolean()
3725
+ import_zod13.z.array(
3726
+ import_zod13.z.object({
3727
+ phase: import_zod13.z.string(),
3728
+ field: import_zod13.z.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
3729
+ value: import_zod13.z.boolean()
3385
3730
  })
3386
3731
  ).optional()
3387
3732
  ).describe("Batch updates \u2014 apply multiple field changes in one atomic read\u2192modify\u2192write")
@@ -3401,7 +3746,7 @@ function registerPipelineTools(server2) {
3401
3746
  "stackwright_pro_check_execution_ready",
3402
3747
  `Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC}`,
3403
3748
  {
3404
- phase: import_zod11.z.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
3749
+ phase: import_zod13.z.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
3405
3750
  },
3406
3751
  async ({ phase }) => res(handleCheckExecutionReady(void 0, phase))
3407
3752
  );
@@ -3421,9 +3766,9 @@ function registerPipelineTools(server2) {
3421
3766
  "stackwright_pro_write_phase_questions",
3422
3767
  `Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC}`,
3423
3768
  {
3424
- phase: import_zod11.z.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
3425
- responseText: import_zod11.z.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
3426
- questions: jsonCoerce(import_zod11.z.array(import_zod11.z.any()).optional()).describe(
3769
+ phase: import_zod13.z.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
3770
+ responseText: import_zod13.z.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
3771
+ questions: jsonCoerce(import_zod13.z.array(import_zod13.z.any()).optional()).describe(
3427
3772
  "Questions array for direct specialist write"
3428
3773
  )
3429
3774
  },
@@ -3465,16 +3810,16 @@ function registerPipelineTools(server2) {
3465
3810
  server2.tool(
3466
3811
  "stackwright_pro_build_specialist_prompt",
3467
3812
  `Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC}`,
3468
- { phase: import_zod11.z.string().describe('Phase to build prompt for, e.g. "pages"') },
3813
+ { phase: import_zod13.z.string().describe('Phase to build prompt for, e.g. "pages"') },
3469
3814
  async ({ phase }) => res(handleBuildSpecialistPrompt({ phase }))
3470
3815
  );
3471
3816
  server2.tool(
3472
3817
  "stackwright_pro_validate_artifact",
3473
3818
  `Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC}`,
3474
3819
  {
3475
- phase: import_zod11.z.string().describe('Phase that produced this artifact, e.g. "designer"'),
3476
- responseText: import_zod11.z.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
3477
- artifact: jsonCoerce(import_zod11.z.record(import_zod11.z.string(), import_zod11.z.unknown()).optional()).describe(
3820
+ phase: import_zod13.z.string().describe('Phase that produced this artifact, e.g. "designer"'),
3821
+ responseText: import_zod13.z.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
3822
+ artifact: jsonCoerce(import_zod13.z.record(import_zod13.z.string(), import_zod13.z.unknown()).optional()).describe(
3478
3823
  "Artifact object to validate and write directly (specialist direct path \u2014 skips off-script detection and JSON parsing)"
3479
3824
  )
3480
3825
  },
@@ -3490,7 +3835,7 @@ function registerPipelineTools(server2) {
3490
3835
  }
3491
3836
 
3492
3837
  // src/tools/safe-write.ts
3493
- var import_zod12 = require("zod");
3838
+ var import_zod14 = require("zod");
3494
3839
  var import_fs6 = require("fs");
3495
3840
  var import_path6 = require("path");
3496
3841
  var OTTER_WRITE_ALLOWLISTS = {
@@ -3566,11 +3911,20 @@ var OTTER_WRITE_ALLOWLISTS = {
3566
3911
  suffix: "",
3567
3912
  description: "Stackwright config (navigation updates)"
3568
3913
  },
3914
+ {
3915
+ prefix: "stackwright.navigation.",
3916
+ suffix: ".yml",
3917
+ description: "Navigation config YAML (merged at build time)"
3918
+ },
3569
3919
  { prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
3570
3920
  { prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
3571
3921
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" },
3572
3922
  { prefix: "README.md", suffix: "", description: "Project README rewrite" }
3573
3923
  ],
3924
+ // domain-expert-otter is a reader/answerer only — it writes via stackwright_pro_save_phase_answers,
3925
+ // not via safe_write. Entry required here because the MCP tool availability guard boilerplate
3926
+ // in its system prompt mentions stackwright_pro_safe_write (triggering the coherence test).
3927
+ "stackwright-pro-domain-expert-otter": [],
3574
3928
  "stackwright-services-otter": [
3575
3929
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Services config artifact" },
3576
3930
  { prefix: "services/", suffix: ".ts", description: "Service implementation files" },
@@ -3890,10 +4244,10 @@ function registerSafeWriteTools(server2) {
3890
4244
  "stackwright_pro_safe_write",
3891
4245
  DESC,
3892
4246
  {
3893
- callerOtter: import_zod12.z.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
3894
- filePath: import_zod12.z.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
3895
- content: import_zod12.z.string().describe("File content to write"),
3896
- createDirectories: boolCoerce(import_zod12.z.boolean().optional().default(true)).describe(
4247
+ callerOtter: import_zod14.z.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
4248
+ filePath: import_zod14.z.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
4249
+ content: import_zod14.z.string().describe("File content to write"),
4250
+ createDirectories: boolCoerce(import_zod14.z.boolean().optional().default(true)).describe(
3897
4251
  "Create parent directories if they don't exist. Default: true"
3898
4252
  )
3899
4253
  },
@@ -3910,7 +4264,7 @@ function registerSafeWriteTools(server2) {
3910
4264
  }
3911
4265
 
3912
4266
  // src/tools/auth.ts
3913
- var import_zod13 = require("zod");
4267
+ var import_zod15 = require("zod");
3914
4268
  var import_fs7 = require("fs");
3915
4269
  var import_path7 = require("path");
3916
4270
  function buildHierarchy(roles) {
@@ -4782,38 +5136,38 @@ function registerAuthTools(server2) {
4782
5136
  "stackwright_pro_configure_auth",
4783
5137
  "Generate authentication middleware and configuration for a Next.js Stackwright application. Writes `middleware.ts` from a secure template, appends/updates the `auth:` section in `stackwright.yml`, and generates `.env.example` with required environment variables. \u26A0\uFE0F For CAC/PKI: generated `middleware.ts` carries a SECURITY REVIEW REQUIRED comment \u2014 certificate chain validation must be verified by a DoD security officer before production deployment. This is the ONLY approved path to generating `middleware.ts`. Never write TypeScript auth files directly.",
4784
5138
  {
4785
- method: import_zod13.z.enum(["cac", "oidc", "oauth2", "none"]),
4786
- provider: import_zod13.z.enum(["azure-ad", "okta", "ping", "cognito", "custom"]).optional(),
5139
+ method: import_zod15.z.enum(["cac", "oidc", "oauth2", "none"]),
5140
+ provider: import_zod15.z.enum(["azure-ad", "okta", "ping", "cognito", "custom"]).optional(),
4787
5141
  // CAC
4788
- cacCaBundle: import_zod13.z.string().optional(),
4789
- cacEdipiLookup: import_zod13.z.string().optional(),
4790
- cacOcspEndpoint: import_zod13.z.string().optional(),
4791
- cacCertHeader: import_zod13.z.string().optional(),
5142
+ cacCaBundle: import_zod15.z.string().optional(),
5143
+ cacEdipiLookup: import_zod15.z.string().optional(),
5144
+ cacOcspEndpoint: import_zod15.z.string().optional(),
5145
+ cacCertHeader: import_zod15.z.string().optional(),
4792
5146
  // OIDC
4793
- oidcDiscoveryUrl: import_zod13.z.string().optional(),
4794
- oidcClientId: import_zod13.z.string().optional(),
4795
- oidcClientSecret: import_zod13.z.string().optional(),
4796
- oidcScopes: import_zod13.z.string().optional(),
4797
- oidcRoleClaim: import_zod13.z.string().optional(),
5147
+ oidcDiscoveryUrl: import_zod15.z.string().optional(),
5148
+ oidcClientId: import_zod15.z.string().optional(),
5149
+ oidcClientSecret: import_zod15.z.string().optional(),
5150
+ oidcScopes: import_zod15.z.string().optional(),
5151
+ oidcRoleClaim: import_zod15.z.string().optional(),
4798
5152
  // OAuth2
4799
- oauth2AuthUrl: import_zod13.z.string().optional(),
4800
- oauth2TokenUrl: import_zod13.z.string().optional(),
4801
- oauth2ClientId: import_zod13.z.string().optional(),
4802
- oauth2ClientSecret: import_zod13.z.string().optional(),
4803
- oauth2Scopes: import_zod13.z.string().optional(),
5153
+ oauth2AuthUrl: import_zod15.z.string().optional(),
5154
+ oauth2TokenUrl: import_zod15.z.string().optional(),
5155
+ oauth2ClientId: import_zod15.z.string().optional(),
5156
+ oauth2ClientSecret: import_zod15.z.string().optional(),
5157
+ oauth2Scopes: import_zod15.z.string().optional(),
4804
5158
  // RBAC
4805
- rbacRoles: jsonCoerce(import_zod13.z.array(import_zod13.z.string()).optional()),
4806
- rbacDefaultRole: import_zod13.z.string().optional(),
5159
+ rbacRoles: jsonCoerce(import_zod15.z.array(import_zod15.z.string()).optional()),
5160
+ rbacDefaultRole: import_zod15.z.string().optional(),
4807
5161
  // Audit
4808
- auditEnabled: boolCoerce(import_zod13.z.boolean().optional()),
4809
- auditRetentionDays: numCoerce(import_zod13.z.number().int().positive().optional()),
5162
+ auditEnabled: boolCoerce(import_zod15.z.boolean().optional()),
5163
+ auditRetentionDays: numCoerce(import_zod15.z.number().int().positive().optional()),
4810
5164
  // Routes
4811
- protectedRoutes: jsonCoerce(import_zod13.z.array(import_zod13.z.string()).optional()),
5165
+ protectedRoutes: jsonCoerce(import_zod15.z.array(import_zod15.z.string()).optional()),
4812
5166
  // Injection for tests
4813
- _cwd: import_zod13.z.string().optional(),
4814
- devOnly: boolCoerce(import_zod13.z.boolean().optional()),
4815
- mockUsers: jsonCoerce(import_zod13.z.array(import_zod13.z.object({ name: import_zod13.z.string(), email: import_zod13.z.string() })).optional()),
4816
- nextMajorVersion: numCoerce(import_zod13.z.number().int().positive().optional())
5167
+ _cwd: import_zod15.z.string().optional(),
5168
+ devOnly: boolCoerce(import_zod15.z.boolean().optional()),
5169
+ mockUsers: jsonCoerce(import_zod15.z.array(import_zod15.z.object({ name: import_zod15.z.string(), email: import_zod15.z.string() })).optional()),
5170
+ nextMajorVersion: numCoerce(import_zod15.z.number().int().positive().optional())
4817
5171
  },
4818
5172
  async (params) => {
4819
5173
  const cwd = params._cwd ?? process.cwd();
@@ -4829,59 +5183,59 @@ var import_path8 = require("path");
4829
5183
  var _checksums = /* @__PURE__ */ new Map([
4830
5184
  [
4831
5185
  "stackwright-pro-api-otter.json",
4832
- "1b9fea59a3e5b1e1015229a4e8d1231b6b87e1d12f23a81ecbbd79fe8657a2e0"
5186
+ "df79f4389a576c2885efa07b04f613c60eb8ebf4a8b1d4c7da5e4bb6ee4248dd"
4833
5187
  ],
4834
5188
  [
4835
5189
  "stackwright-pro-auth-otter.json",
4836
- "f91f82468da9c273fbe6481a3a40957d4c7aa3fa72528c492b6ac5d8e06a563a"
5190
+ "539c7145e88dd2afa7f2417be8cfa8e0e7d82608d051271a20f9c7e4e77d98e7"
4837
5191
  ],
4838
5192
  [
4839
5193
  "stackwright-pro-dashboard-otter.json",
4840
- "9c319d311801730e8dc9bc142eebb8fc5a7f48da48fa0b8d8c3b7431652447be"
5194
+ "160af221e04200f53709f8c3e249ca6dafb38a325b232fabd478b28f5492ab01"
4841
5195
  ],
4842
5196
  [
4843
5197
  "stackwright-pro-data-otter.json",
4844
- "f39061981caf0bd14e4eb83e74ac200450425536d14b8c00d60be94759a1ca9d"
5198
+ "b135cb0013edaa40baaf8a03f1d8795920dc0ce611183f047e55104bf8cf35be"
4845
5199
  ],
4846
5200
  [
4847
5201
  "stackwright-pro-designer-otter.json",
4848
- "af09ac8f06385bdbac63e2820daa2ff7d38b8ff1ff383c161f07e3fb9d9359c5"
5202
+ "1364b2c235c07b0b798e9aab90a68604f60019a5508d41ba576977f173e20cd9"
4849
5203
  ],
4850
5204
  [
4851
5205
  "stackwright-pro-domain-expert-otter.json",
4852
- "41e3a5838f05f6a51ed272860dd2d5b1df1f20bfc847eb8f39be109b89738e99"
5206
+ "1f21b8ff3450bdae29a4d31b31462ba22583995a448af3c11ab0a5071f46bd72"
4853
5207
  ],
4854
5208
  [
4855
5209
  "stackwright-pro-foreman-otter.json",
4856
- "4948b8189223e4ced81b632cca7d14b79fde85f204483ab8e847182692c4ee57"
5210
+ "438b03d2c35f64536055c68b3a9044fe3b5e4f2889acde4500dd801fad724be1"
4857
5211
  ],
4858
5212
  [
4859
5213
  "stackwright-pro-geo-otter.json",
4860
- "ff1191af8108cc70c09afe6a99b008dcb46f71e20f884da1ee424e431868a1b6"
5214
+ "2ec83c26a08c413d9553ff8b8f0f0f643c1a8da043741b356e27106cad78f05f"
4861
5215
  ],
4862
5216
  [
4863
5217
  "stackwright-pro-page-otter.json",
4864
- "cdd878857c1d809c60263693ab0c6cbb45087fd9c3eeda5b4628bd7026d2bded"
5218
+ "0057ea97f7c2e33a8673140f59a0237eb627d82c676af3fae4faa31033598e03"
4865
5219
  ],
4866
5220
  [
4867
5221
  "stackwright-pro-polish-otter.json",
4868
- "e5f88d054dd536646f48e5d47cbd64f825f493100002309b90c912fa69ef279e"
5222
+ "02cfa56354bfcd33af6d634ac647f38812a2353e487816b67edc2e9eb2c3e357"
4869
5223
  ],
4870
5224
  [
4871
5225
  "stackwright-pro-scaffold-otter.json",
4872
- "c9950a34cc6a729a2ad22a4052afc3d7f9b033fe390d9edba4014b76e1d076b6"
5226
+ "ccbc9d20a65dcf7cc2eacb5ac3060cd95a10a3ef795883170684658872299e7b"
4873
5227
  ],
4874
5228
  [
4875
5229
  "stackwright-pro-theme-otter.json",
4876
- "532d9ca7db6911a09ce5029a8c74c89360bdf6cb8ede8c2636866b509cbe06f9"
5230
+ "a271eac375db9d014afb781536f60f2e6716a9054b12b9c35713af09a63912ff"
4877
5231
  ],
4878
5232
  [
4879
5233
  "stackwright-pro-workflow-otter.json",
4880
- "80eb6c8d3f079e00533dcb4a3c7fd6baae4d5f4192ab6c27c85d2de5359592fd"
5234
+ "6cc800374de6e723a283e4757af97f7b8337c64ee78053d7e50a79e17314a049"
4881
5235
  ],
4882
5236
  [
4883
5237
  "stackwright-services-otter.json",
4884
- "b8a252d6a2c5090138899925d3ea95bf3339b6e49adc276a1f5f7304b4ae5134"
5238
+ "c013d7fc2475e62d0af54d57bc988182feee7766bac0edf841cbfad5c73e9261"
4885
5239
  ]
4886
5240
  ]);
4887
5241
  Object.freeze(_checksums);
@@ -5097,7 +5451,7 @@ function registerIntegrityTools(server2) {
5097
5451
  }
5098
5452
 
5099
5453
  // src/tools/domain.ts
5100
- var import_zod14 = require("zod");
5454
+ var import_zod16 = require("zod");
5101
5455
  var import_fs9 = require("fs");
5102
5456
  var import_path9 = require("path");
5103
5457
  function handleListCollections(input) {
@@ -5483,7 +5837,7 @@ function registerDomainTools(server2) {
5483
5837
  "stackwright_pro_resolve_data_strategy",
5484
5838
  "Look up the data freshness strategy configuration from the user's answer. Returns mechanism, revalidation seconds, required packages, and the exact MCP tool call to make. Replaces the strategy table in the data-otter prompt.",
5485
5839
  {
5486
- strategy: import_zod14.z.string().describe(
5840
+ strategy: import_zod16.z.string().describe(
5487
5841
  'The data-1 answer value: "pulse-fast", "isr-fast", "isr-standard", or "isr-slow"'
5488
5842
  )
5489
5843
  },
@@ -5493,7 +5847,7 @@ function registerDomainTools(server2) {
5493
5847
  "stackwright_pro_validate_workflow",
5494
5848
  "Validate a workflow definition against the Stackwright workflow schema. Checks step ID uniqueness, transition targets, terminal state existence, and service references. Call this after the workflow otter produces output.",
5495
5849
  {
5496
- workflow: jsonCoerce(import_zod14.z.record(import_zod14.z.string(), import_zod14.z.unknown()).optional()).describe(
5850
+ workflow: jsonCoerce(import_zod16.z.record(import_zod16.z.string(), import_zod16.z.unknown()).optional()).describe(
5497
5851
  "Parsed workflow object. If omitted, reads from .stackwright/artifacts/workflow-config.json"
5498
5852
  )
5499
5853
  },
@@ -5502,7 +5856,7 @@ function registerDomainTools(server2) {
5502
5856
  }
5503
5857
 
5504
5858
  // src/tools/type-schemas.ts
5505
- var import_zod15 = require("zod");
5859
+ var import_zod17 = require("zod");
5506
5860
  function buildTypeSchemaSummary() {
5507
5861
  return {
5508
5862
  version: "1.0",
@@ -5579,7 +5933,7 @@ function registerTypeSchemasTool(server2) {
5579
5933
  "stackwright_pro_get_type_schemas",
5580
5934
  "Returns a structured summary of all canonical @stackwright-pro/types schemas, organized by domain. Use this to determine which otter owns a given schema and what artifact key to expect.",
5581
5935
  {
5582
- format: import_zod15.z.enum(["full", "domains-only"]).optional().default("full").describe("full = complete summary with all fields; domains-only = just domain names")
5936
+ format: import_zod17.z.enum(["full", "domains-only"]).optional().default("full").describe("full = complete summary with all fields; domains-only = just domain names")
5583
5937
  },
5584
5938
  async ({ format }) => {
5585
5939
  const summary = buildTypeSchemaSummary();
@@ -5768,7 +6122,7 @@ function registerScaffoldCleanupTools(server2) {
5768
6122
  }
5769
6123
 
5770
6124
  // src/tools/contrast.ts
5771
- var import_zod16 = require("zod");
6125
+ var import_zod18 = require("zod");
5772
6126
  function linearizeChannel(c255) {
5773
6127
  const c = c255 / 255;
5774
6128
  return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
@@ -5940,8 +6294,8 @@ function registerContrastTools(server2) {
5940
6294
  "stackwright_pro_check_contrast",
5941
6295
  'Compute the exact WCAG 2.1 contrast ratio between a foreground and background color. Returns the ratio and AA/AAA pass/fail flags. Use this to verify any foreground/background color pair before writing it to the token set. Accepts #RGB, #RRGGBB, hsl(...), or shadcn HSL format ("H S% L%").',
5942
6296
  {
5943
- fg: import_zod16.z.string().describe("Foreground (text) color \u2014 hex or HSL"),
5944
- bg: import_zod16.z.string().describe("Background color \u2014 hex or HSL")
6297
+ fg: import_zod18.z.string().describe("Foreground (text) color \u2014 hex or HSL"),
6298
+ bg: import_zod18.z.string().describe("Background color \u2014 hex or HSL")
5945
6299
  },
5946
6300
  async ({ fg, bg }) => {
5947
6301
  let result;
@@ -5971,8 +6325,8 @@ function registerContrastTools(server2) {
5971
6325
  "stackwright_pro_derive_accessible_palette",
5972
6326
  'Given a seed color (treated as a background), derive the best accessible foreground (text) color that meets the target contrast ratio. Evaluates #ffffff and #000000 against the seed and picks whichever satisfies the target ratio with the highest contrast. Use this for every {name}-foreground token derivation instead of guessing white vs black. Accepts #RGB, #RRGGBB, hsl(...), or shadcn HSL format ("H S% L%").',
5973
6327
  {
5974
- seed: import_zod16.z.string().describe("Background (seed) color \u2014 hex or HSL"),
5975
- targetRatio: numCoerce(import_zod16.z.number().default(4.5)).describe(
6328
+ seed: import_zod18.z.string().describe("Background (seed) color \u2014 hex or HSL"),
6329
+ targetRatio: numCoerce(import_zod18.z.number().default(4.5)).describe(
5976
6330
  "Minimum required contrast ratio (default 4.5 for WCAG AA)"
5977
6331
  )
5978
6332
  },
@@ -6009,9 +6363,11 @@ function registerContrastTools(server2) {
6009
6363
  // package.json
6010
6364
  var package_default = {
6011
6365
  dependencies: {
6012
- "@stackwright-pro/types": "workspace:*",
6013
6366
  "@modelcontextprotocol/sdk": "^1.10.0",
6014
6367
  "@stackwright-pro/cli-data-explorer": "workspace:*",
6368
+ "@stackwright-pro/types": "workspace:*",
6369
+ "@types/js-yaml": "^4.0.9",
6370
+ "js-yaml": "^4.2.0",
6015
6371
  zod: "^4.4.3"
6016
6372
  },
6017
6373
  devDependencies: {
@@ -6029,7 +6385,7 @@ var package_default = {
6029
6385
  "test:coverage": "vitest run --coverage"
6030
6386
  },
6031
6387
  name: "@stackwright-pro/mcp",
6032
- version: "0.2.0-alpha.80",
6388
+ version: "0.2.0-alpha.82",
6033
6389
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
6034
6390
  license: "SEE LICENSE IN LICENSE",
6035
6391
  main: "./dist/server.js",
@@ -6083,6 +6439,8 @@ registerIntegrityTools(server);
6083
6439
  registerArtifactSigningTools(server);
6084
6440
  registerDomainTools(server);
6085
6441
  registerTypeSchemasTool(server);
6442
+ registerValidateYamlFragmentTool(server);
6443
+ registerGetSchemaTool(server);
6086
6444
  registerScaffoldCleanupTools(server);
6087
6445
  registerContrastTools(server);
6088
6446
  async function main() {