@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.mjs CHANGED
@@ -1589,24 +1589,28 @@ import { join as join2 } from "path";
1589
1589
  var OTTER_NAME_TO_PHASE = [
1590
1590
  ["designer", "designer"],
1591
1591
  ["theme", "theme"],
1592
+ ["scaffold", "scaffold"],
1592
1593
  ["api", "api"],
1593
1594
  ["auth", "auth"],
1594
1595
  ["dashboard", "dashboard"],
1595
1596
  ["data", "data"],
1596
1597
  ["page", "pages"],
1597
1598
  ["workflow", "workflow"],
1599
+ ["services", "services"],
1598
1600
  ["polish", "polish"],
1599
1601
  ["geo", "geo"]
1600
1602
  ];
1601
1603
  var PHASE_TO_OTTER = {
1602
1604
  designer: "stackwright-pro-designer-otter",
1603
1605
  theme: "stackwright-pro-theme-otter",
1606
+ scaffold: "stackwright-pro-scaffold-otter",
1604
1607
  api: "stackwright-pro-api-otter",
1605
1608
  auth: "stackwright-pro-auth-otter",
1606
1609
  pages: "stackwright-pro-page-otter",
1607
1610
  dashboard: "stackwright-pro-dashboard-otter",
1608
1611
  data: "stackwright-pro-data-otter",
1609
1612
  workflow: "stackwright-pro-workflow-otter",
1613
+ services: "stackwright-services-otter",
1610
1614
  polish: "stackwright-pro-polish-otter",
1611
1615
  geo: "stackwright-pro-geo-otter"
1612
1616
  };
@@ -1991,7 +1995,7 @@ function registerOrchestrationTools(server2) {
1991
1995
  }
1992
1996
 
1993
1997
  // src/tools/pipeline.ts
1994
- import { z as z11 } from "zod";
1998
+ import { z as z13 } from "zod";
1995
1999
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync4, lstatSync as lstatSync5 } from "fs";
1996
2000
  import { join as join4 } from "path";
1997
2001
  import { createHash as createHash3 } from "crypto";
@@ -2335,7 +2339,355 @@ function registerArtifactSigningTools(server2) {
2335
2339
  }
2336
2340
 
2337
2341
  // src/tools/pipeline.ts
2338
- import { WorkflowFileSchema, authConfigSchema } from "@stackwright-pro/types";
2342
+ import { WorkflowFileSchema, authConfigSchema as authConfigSchema3 } from "@stackwright-pro/types";
2343
+
2344
+ // src/tools/get-schema.ts
2345
+ import { z as z12 } from "zod";
2346
+ import {
2347
+ WorkflowStepSchema as WorkflowStepSchema2,
2348
+ WorkflowDefinitionSchema as WorkflowDefinitionSchema2,
2349
+ WorkflowFieldSchema as WorkflowFieldSchema2,
2350
+ WorkflowActionSchema as WorkflowActionSchema2,
2351
+ authConfigSchema as authConfigSchema2
2352
+ } from "@stackwright-pro/types";
2353
+
2354
+ // src/tools/validate-yaml-fragment.ts
2355
+ import { z as z11 } from "zod";
2356
+ import { load as yamlLoad } from "js-yaml";
2357
+ import {
2358
+ WorkflowStepSchema,
2359
+ WorkflowDefinitionSchema,
2360
+ WorkflowFieldSchema,
2361
+ WorkflowActionSchema,
2362
+ authConfigSchema
2363
+ } from "@stackwright-pro/types";
2364
+ var SUPPORTED_SCHEMA_NAMES = [
2365
+ "workflow_step",
2366
+ "workflow_definition",
2367
+ "workflow_field",
2368
+ "workflow_action",
2369
+ "navigation_item",
2370
+ "auth_config"
2371
+ ];
2372
+ var NavigationLinkSchema = z11.lazy(
2373
+ () => z11.object({
2374
+ label: z11.string().min(1),
2375
+ href: z11.string().min(1),
2376
+ children: z11.array(NavigationLinkSchema).optional()
2377
+ })
2378
+ );
2379
+ var NavigationSectionSchema = z11.object({
2380
+ section: z11.string().min(1),
2381
+ items: z11.array(NavigationLinkSchema)
2382
+ });
2383
+ var NavigationItemSchema = z11.union([
2384
+ NavigationLinkSchema,
2385
+ NavigationSectionSchema
2386
+ ]);
2387
+ var FIELD_SYNONYMS = {
2388
+ workflow_step: {
2389
+ title: "label",
2390
+ name: "label (at step level, use label: not name:)",
2391
+ description: "message (use message: for step-level descriptive text)",
2392
+ style: 'theme.variant \u2014 use theme: { variant: "primary" } not style: "primary"',
2393
+ transitions: 'on_submit.transition \u2014 use on_submit: { transition: "next_step" } not transitions: [{then:...}]'
2394
+ },
2395
+ workflow_definition: {
2396
+ title: "label",
2397
+ name: "label (workflow-level label, not name:)",
2398
+ description: "description is valid here \u2014 but message: is not (that is a step field)",
2399
+ type: "id (workflows are identified by id:, not type:)"
2400
+ },
2401
+ workflow_field: {
2402
+ id: "name (field-level identifier is name:, NOT id:)",
2403
+ title: "label (field display text is label:, NOT title:)",
2404
+ description: "placeholder or label (no description field on WorkflowField)"
2405
+ },
2406
+ workflow_action: {
2407
+ style: 'theme.variant \u2014 use theme: { variant: "primary" } not style: "primary"',
2408
+ then: "transition (action-level next-step is transition:, NOT then:)",
2409
+ on_click: 'action: "service:..." (extract the service reference from on_click.action)',
2410
+ title: "label (action display text is label:, NOT title:)",
2411
+ name: "label (action display text is label:, NOT name:)"
2412
+ },
2413
+ navigation_item: {
2414
+ children: "items (NavigationSection uses items: not children: \u2014 also use section: instead of label: for section headers)",
2415
+ label: 'section (if this is a nav group/section, use section: "Title" and items: [...], not label:)',
2416
+ title: "section (NavigationSection header field is section:, NOT title:)"
2417
+ },
2418
+ auth_config: {
2419
+ method: 'type \u2014 auth config discriminates on type: "oidc" | "pki", NOT method:',
2420
+ provider_type: 'type \u2014 use type: "oidc" or type: "pki"',
2421
+ strategy: "type \u2014 top-level discriminator is type:, not strategy:"
2422
+ }
2423
+ };
2424
+ function getSchema(name) {
2425
+ switch (name) {
2426
+ case "workflow_step":
2427
+ return WorkflowStepSchema;
2428
+ case "workflow_definition":
2429
+ return WorkflowDefinitionSchema;
2430
+ case "workflow_field":
2431
+ return WorkflowFieldSchema;
2432
+ case "workflow_action":
2433
+ return WorkflowActionSchema;
2434
+ case "navigation_item":
2435
+ return NavigationItemSchema;
2436
+ case "auth_config":
2437
+ return authConfigSchema;
2438
+ }
2439
+ }
2440
+ function enrichErrors(issues, synonyms) {
2441
+ return issues.map((issue) => {
2442
+ const pathStr = issue.path.join(".");
2443
+ const lastSegment = issue.path[issue.path.length - 1];
2444
+ const fieldName = typeof lastSegment === "string" ? lastSegment : void 0;
2445
+ const didYouMean = fieldName ? synonyms[fieldName] : void 0;
2446
+ return {
2447
+ path: pathStr || "(root)",
2448
+ message: issue.message,
2449
+ ...didYouMean ? { didYouMean } : {}
2450
+ };
2451
+ });
2452
+ }
2453
+ function handleValidateYamlFragment(input) {
2454
+ const { schemaName, yaml } = input;
2455
+ if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
2456
+ return {
2457
+ valid: false,
2458
+ parseError: `Unknown schemaName: "${schemaName}". Supported: ${SUPPORTED_SCHEMA_NAMES.join(", ")}`
2459
+ };
2460
+ }
2461
+ const name = schemaName;
2462
+ let parsed;
2463
+ try {
2464
+ parsed = yamlLoad(yaml);
2465
+ } catch (err) {
2466
+ return {
2467
+ valid: false,
2468
+ parseError: `YAML parse error: ${err instanceof Error ? err.message : String(err)}`
2469
+ };
2470
+ }
2471
+ const schema = getSchema(name);
2472
+ const result = schema.safeParse(parsed);
2473
+ if (result.success) {
2474
+ return { valid: true };
2475
+ }
2476
+ const synonyms = FIELD_SYNONYMS[name];
2477
+ const errors = enrichErrors(result.error.issues, synonyms);
2478
+ return { valid: false, errors };
2479
+ }
2480
+ function registerValidateYamlFragmentTool(server2) {
2481
+ server2.tool(
2482
+ "stackwright_pro_validate_yaml_fragment",
2483
+ '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.',
2484
+ {
2485
+ schemaName: z11.enum(SUPPORTED_SCHEMA_NAMES).describe(
2486
+ "Schema to validate against. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
2487
+ ),
2488
+ yaml: z11.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
2489
+ },
2490
+ async ({ schemaName, yaml }) => {
2491
+ const result = handleValidateYamlFragment({ schemaName, yaml });
2492
+ return {
2493
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2494
+ };
2495
+ }
2496
+ );
2497
+ }
2498
+
2499
+ // src/tools/get-schema.ts
2500
+ var NavigationLinkSchema2 = z12.lazy(
2501
+ () => z12.object({
2502
+ label: z12.string().min(1),
2503
+ href: z12.string().min(1),
2504
+ children: z12.array(NavigationLinkSchema2).optional()
2505
+ })
2506
+ );
2507
+ var NavigationSectionSchema2 = z12.object({
2508
+ section: z12.string().min(1),
2509
+ items: z12.array(NavigationLinkSchema2)
2510
+ });
2511
+ var NavigationItemSchema2 = z12.union([
2512
+ NavigationLinkSchema2,
2513
+ NavigationSectionSchema2
2514
+ ]);
2515
+ function getZodSchema(name) {
2516
+ switch (name) {
2517
+ case "workflow_step":
2518
+ return WorkflowStepSchema2;
2519
+ case "workflow_definition":
2520
+ return WorkflowDefinitionSchema2;
2521
+ case "workflow_field":
2522
+ return WorkflowFieldSchema2;
2523
+ case "workflow_action":
2524
+ return WorkflowActionSchema2;
2525
+ case "navigation_item":
2526
+ return NavigationItemSchema2;
2527
+ case "auth_config":
2528
+ return authConfigSchema2;
2529
+ }
2530
+ }
2531
+ function formatType(node, depth = 0) {
2532
+ if (node.const !== void 0) return JSON.stringify(node.const);
2533
+ if (node.enum) return node.enum.map((v) => JSON.stringify(v)).join(" | ");
2534
+ if (node.oneOf || node.anyOf) {
2535
+ const branches = node.oneOf ?? node.anyOf ?? [];
2536
+ return branches.map((b) => {
2537
+ const disc = b.properties ? Object.entries(b.properties).filter(([, v]) => v.const !== void 0).map(([k, v]) => `${k}: ${JSON.stringify(v.const)}`).join(", ") : "";
2538
+ return disc ? `{ ${disc}, ... }` : "{ ... }";
2539
+ }).join(" | ");
2540
+ }
2541
+ if (node.type === "array" && node.items) {
2542
+ return `${formatType(node.items, depth)}[]`;
2543
+ }
2544
+ if (node.type === "object" && node.properties && depth < 2) {
2545
+ const inner = Object.entries(node.properties).map(([k, v]) => `${k}: ${formatType(v, depth + 1)}`).join(", ");
2546
+ return `{ ${inner} }`;
2547
+ }
2548
+ const base = Array.isArray(node.type) ? node.type.join(" | ") : node.type ?? "unknown";
2549
+ const constraints = [];
2550
+ if (node.maxLength !== void 0) constraints.push(`max ${node.maxLength} chars`);
2551
+ if (node.minLength !== void 0) constraints.push(`min ${node.minLength} chars`);
2552
+ if (node.pattern) constraints.push(`pattern: ${node.pattern}`);
2553
+ if (node.minimum !== void 0) constraints.push(`min ${node.minimum}`);
2554
+ if (node.maximum !== void 0) constraints.push(`max ${node.maximum}`);
2555
+ return constraints.length > 0 ? `${base} (${constraints.join(", ")})` : base;
2556
+ }
2557
+ function formatObjectSchema(node, schemaName, synonyms) {
2558
+ const lines = [];
2559
+ const displayName = schemaName.split("_").map((w) => w[0].toUpperCase() + w.slice(1)).join("");
2560
+ lines.push(`${displayName}:`);
2561
+ if (node.oneOf || node.anyOf) {
2562
+ const branches = node.oneOf ?? node.anyOf ?? [];
2563
+ lines.push(" Discriminated union \u2014 choose one variant:");
2564
+ for (const branch of branches) {
2565
+ const req = new Set(branch.required ?? []);
2566
+ const disc = Object.entries(branch.properties ?? {}).filter(([, v]) => v.const !== void 0).map(([k, v]) => `${k}: ${JSON.stringify(v.const)}`).join(", ");
2567
+ lines.push(` VARIANT (${disc}):`);
2568
+ for (const [field, fieldNode] of Object.entries(branch.properties ?? {})) {
2569
+ if (fieldNode.const !== void 0) continue;
2570
+ const isRequired = req.has(field);
2571
+ const hint = synonyms[field] ? ` -- WARNING: use ${field}: not ${synonyms[field]}` : "";
2572
+ lines.push(
2573
+ ` ${isRequired ? "REQUIRED" : "optional"}: ${field}: ${formatType(fieldNode)}${hint}`
2574
+ );
2575
+ }
2576
+ }
2577
+ return lines.join("\n");
2578
+ }
2579
+ const required = new Set(node.required ?? []);
2580
+ const properties = node.properties ?? {};
2581
+ const requiredFields = Object.entries(properties).filter(([k]) => required.has(k));
2582
+ const optionalFields = Object.entries(properties).filter(([k]) => !required.has(k));
2583
+ if (requiredFields.length > 0) {
2584
+ lines.push(" REQUIRED:");
2585
+ for (const [field, fieldNode] of requiredFields) {
2586
+ const typeStr = formatType(fieldNode);
2587
+ const warn = synonyms[field] ? ` -- CAUTION: this schema uses ${field}` : "";
2588
+ lines.push(` ${field}: ${typeStr}${warn}`);
2589
+ }
2590
+ }
2591
+ if (optionalFields.length > 0) {
2592
+ lines.push(" OPTIONAL:");
2593
+ for (const [field, fieldNode] of optionalFields) {
2594
+ const typeStr = formatType(fieldNode);
2595
+ lines.push(` ${field}: ${typeStr}`);
2596
+ }
2597
+ }
2598
+ return lines.join("\n");
2599
+ }
2600
+ function handleGetSchema(input) {
2601
+ const { schemaName } = input;
2602
+ if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
2603
+ return {
2604
+ error: `Unknown schemaName: "${schemaName}". Supported: ${SUPPORTED_SCHEMA_NAMES.join(", ")}`
2605
+ };
2606
+ }
2607
+ const name = schemaName;
2608
+ const schema = getZodSchema(name);
2609
+ const synonyms = FIELD_SYNONYMS[name];
2610
+ let jsonSchema;
2611
+ try {
2612
+ const raw = z12.toJSONSchema(schema, { unrepresentable: "any" });
2613
+ jsonSchema = raw;
2614
+ } catch {
2615
+ jsonSchema = { type: "object", description: "Schema introspection unavailable for this type" };
2616
+ }
2617
+ const summary = formatObjectSchema(jsonSchema, name, synonyms);
2618
+ const synonymWarnings = Object.entries(synonyms).map(
2619
+ ([wrong, correct]) => ` WARNING: use "${correct}" \u2014 NOT "${wrong}"`
2620
+ );
2621
+ const fullText = [
2622
+ summary,
2623
+ "",
2624
+ "FIELD NAME WARNINGS (common drift patterns):",
2625
+ ...synonymWarnings
2626
+ ].join("\n");
2627
+ const tokenEstimate = Math.ceil(fullText.length / 4);
2628
+ return {
2629
+ schemaName: name,
2630
+ summary,
2631
+ synonymWarnings,
2632
+ tokenEstimate
2633
+ };
2634
+ }
2635
+ var PHASE_SCHEMA_NAMES = {
2636
+ workflow: ["workflow_step", "workflow_definition", "workflow_field", "workflow_action"],
2637
+ pages: ["navigation_item"],
2638
+ dashboard: ["navigation_item"],
2639
+ polish: ["navigation_item"],
2640
+ auth: ["auth_config"]
2641
+ };
2642
+ function buildSchemaReferenceBlock(phase) {
2643
+ const schemaNames = PHASE_SCHEMA_NAMES[phase];
2644
+ if (!schemaNames || schemaNames.length === 0) return null;
2645
+ const sections = [
2646
+ "CANONICAL_SCHEMA_REFERENCE (authoritative \u2014 use exact field names below):",
2647
+ ""
2648
+ ];
2649
+ for (const name of schemaNames) {
2650
+ const result = handleGetSchema({ schemaName: name });
2651
+ if ("error" in result) continue;
2652
+ sections.push(result.summary);
2653
+ if (result.synonymWarnings.length > 0) {
2654
+ sections.push(" Common mistakes to avoid:");
2655
+ sections.push(...result.synonymWarnings);
2656
+ }
2657
+ sections.push("");
2658
+ }
2659
+ return sections.join("\n");
2660
+ }
2661
+ function registerGetSchemaTool(server2) {
2662
+ server2.tool(
2663
+ "stackwright_pro_get_schema",
2664
+ '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.',
2665
+ {
2666
+ schemaName: z12.enum(SUPPORTED_SCHEMA_NAMES).describe(
2667
+ "Schema to summarize. One of: workflow_step, workflow_definition, workflow_field, workflow_action, navigation_item, auth_config"
2668
+ )
2669
+ },
2670
+ async ({ schemaName }) => {
2671
+ const result = handleGetSchema({ schemaName });
2672
+ if ("error" in result) {
2673
+ return {
2674
+ content: [{ type: "text", text: JSON.stringify({ error: result.error }) }],
2675
+ isError: true
2676
+ };
2677
+ }
2678
+ return {
2679
+ content: [
2680
+ {
2681
+ type: "text",
2682
+ text: JSON.stringify(result, null, 2)
2683
+ }
2684
+ ]
2685
+ };
2686
+ }
2687
+ );
2688
+ }
2689
+
2690
+ // src/tools/pipeline.ts
2339
2691
  var PHASE_ORDER = [
2340
2692
  "designer",
2341
2693
  "theme",
@@ -2903,6 +3255,10 @@ ${JSON.stringify(content, null, 2)}`
2903
3255
  const artifactSchema = PHASE_ARTIFACT_SCHEMA[phase];
2904
3256
  parts.push("", "REQUIRED_ARTIFACT_SCHEMA:");
2905
3257
  parts.push(artifactSchema);
3258
+ const schemaRef = buildSchemaReferenceBlock(phase);
3259
+ if (schemaRef) {
3260
+ parts.push("", schemaRef);
3261
+ }
2906
3262
  parts.push("", "Execute using these answers and the upstream artifacts provided.");
2907
3263
  const prompt = parts.join("\n");
2908
3264
  const dependenciesSatisfied = missingDependencies.length === 0;
@@ -2937,6 +3293,7 @@ var OFF_SCRIPT_PATTERNS = [
2937
3293
  var PHASE_REQUIRED_KEYS = {
2938
3294
  designer: ["designLanguage", "themeTokenSeeds"],
2939
3295
  theme: ["tokens"],
3296
+ scaffold: ["version", "generatedBy", "appRouterFiles"],
2940
3297
  api: ["entities", "version", "generatedBy"],
2941
3298
  auth: ["version", "generatedBy"],
2942
3299
  data: ["version", "generatedBy", "strategy", "collections"],
@@ -3287,7 +3644,7 @@ function handleValidateArtifact(input) {
3287
3644
  auth: (artifact2) => {
3288
3645
  const authConfig = artifact2["authConfig"];
3289
3646
  if (!authConfig) return { success: true };
3290
- const result = authConfigSchema.safeParse(authConfig);
3647
+ const result = authConfigSchema3.safeParse(authConfig);
3291
3648
  if (!result.success) {
3292
3649
  const issues = result.error.issues.slice(0, 3).map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
3293
3650
  return { success: false, error: { message: issues } };
@@ -3359,21 +3716,21 @@ function registerPipelineTools(server2) {
3359
3716
  "stackwright_pro_set_pipeline_state",
3360
3717
  `Atomic read\u2192modify\u2192write pipeline state. ${DESC}`,
3361
3718
  {
3362
- phase: z11.string().optional().describe('Phase to update, e.g. "designer"'),
3363
- field: z11.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
3364
- value: boolCoerce(z11.boolean().optional()).describe(
3719
+ phase: z13.string().optional().describe('Phase to update, e.g. "designer"'),
3720
+ field: z13.enum(["questionsCollected", "answered", "executed", "artifactWritten"]).optional().describe("Boolean field to set"),
3721
+ value: boolCoerce(z13.boolean().optional()).describe(
3365
3722
  'Value for the field \u2014 must be a JSON boolean (true/false), NOT the string "true"/"false"'
3366
3723
  ),
3367
- status: z11.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level status override"),
3368
- incrementRetry: boolCoerce(z11.boolean().optional()).describe(
3724
+ status: z13.enum(["setup", "questions", "execution", "done"]).optional().describe("Top-level status override"),
3725
+ incrementRetry: boolCoerce(z13.boolean().optional()).describe(
3369
3726
  "Bump retryCount by 1 \u2014 must be a JSON boolean"
3370
3727
  ),
3371
3728
  updates: jsonCoerce(
3372
- z11.array(
3373
- z11.object({
3374
- phase: z11.string(),
3375
- field: z11.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
3376
- value: z11.boolean()
3729
+ z13.array(
3730
+ z13.object({
3731
+ phase: z13.string(),
3732
+ field: z13.enum(["questionsCollected", "answered", "executed", "artifactWritten"]),
3733
+ value: z13.boolean()
3377
3734
  })
3378
3735
  ).optional()
3379
3736
  ).describe("Batch updates \u2014 apply multiple field changes in one atomic read\u2192modify\u2192write")
@@ -3393,7 +3750,7 @@ function registerPipelineTools(server2) {
3393
3750
  "stackwright_pro_check_execution_ready",
3394
3751
  `Check all phases have answer files in .stackwright/answers/. If phase is provided, check only that phase. ${DESC}`,
3395
3752
  {
3396
- phase: z11.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
3753
+ phase: z13.string().optional().describe("If provided, check only this phase's readiness. Omit to check all phases.")
3397
3754
  },
3398
3755
  async ({ phase }) => res(handleCheckExecutionReady(void 0, phase))
3399
3756
  );
@@ -3413,9 +3770,9 @@ function registerPipelineTools(server2) {
3413
3770
  "stackwright_pro_write_phase_questions",
3414
3771
  `Parse otter question-collection response \u2192 .stackwright/questions/{phase}.json. Specialists may also call this directly with a parsed questions array. ${DESC}`,
3415
3772
  {
3416
- phase: z11.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
3417
- responseText: z11.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
3418
- questions: jsonCoerce(z11.array(z11.any()).optional()).describe(
3773
+ phase: z13.string().optional().describe('Phase name, e.g. "designer" (required for direct write)'),
3774
+ responseText: z13.string().optional().describe("Raw LLM response from QUESTION_COLLECTION_MODE"),
3775
+ questions: jsonCoerce(z13.array(z13.any()).optional()).describe(
3419
3776
  "Questions array for direct specialist write"
3420
3777
  )
3421
3778
  },
@@ -3457,16 +3814,16 @@ function registerPipelineTools(server2) {
3457
3814
  server2.tool(
3458
3815
  "stackwright_pro_build_specialist_prompt",
3459
3816
  `Assemble execution prompt from answers + upstream artifacts. Foreman passes verbatim. ${DESC}`,
3460
- { phase: z11.string().describe('Phase to build prompt for, e.g. "pages"') },
3817
+ { phase: z13.string().describe('Phase to build prompt for, e.g. "pages"') },
3461
3818
  async ({ phase }) => res(handleBuildSpecialistPrompt({ phase }))
3462
3819
  );
3463
3820
  server2.tool(
3464
3821
  "stackwright_pro_validate_artifact",
3465
3822
  `Validate and write artifact to .stackwright/artifacts/. Returns retryPrompt on failure. ${DESC}`,
3466
3823
  {
3467
- phase: z11.string().describe('Phase that produced this artifact, e.g. "designer"'),
3468
- responseText: z11.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
3469
- artifact: jsonCoerce(z11.record(z11.string(), z11.unknown()).optional()).describe(
3824
+ phase: z13.string().describe('Phase that produced this artifact, e.g. "designer"'),
3825
+ responseText: z13.string().optional().describe("Raw response text from the specialist otter (Foreman-mediated path, legacy)"),
3826
+ artifact: jsonCoerce(z13.record(z13.string(), z13.unknown()).optional()).describe(
3470
3827
  "Artifact object to validate and write directly (specialist direct path \u2014 skips off-script detection and JSON parsing)"
3471
3828
  )
3472
3829
  },
@@ -3482,7 +3839,7 @@ function registerPipelineTools(server2) {
3482
3839
  }
3483
3840
 
3484
3841
  // src/tools/safe-write.ts
3485
- import { z as z12 } from "zod";
3842
+ import { z as z14 } from "zod";
3486
3843
  import { writeFileSync as writeFileSync5, readFileSync as readFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync5, lstatSync as lstatSync6 } from "fs";
3487
3844
  import { normalize, isAbsolute, dirname, join as join5 } from "path";
3488
3845
  var OTTER_WRITE_ALLOWLISTS = {
@@ -3558,11 +3915,20 @@ var OTTER_WRITE_ALLOWLISTS = {
3558
3915
  suffix: "",
3559
3916
  description: "Stackwright config (navigation updates)"
3560
3917
  },
3918
+ {
3919
+ prefix: "stackwright.navigation.",
3920
+ suffix: ".yml",
3921
+ description: "Navigation config YAML (merged at build time)"
3922
+ },
3561
3923
  { prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
3562
3924
  { prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
3563
3925
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" },
3564
3926
  { prefix: "README.md", suffix: "", description: "Project README rewrite" }
3565
3927
  ],
3928
+ // domain-expert-otter is a reader/answerer only — it writes via stackwright_pro_save_phase_answers,
3929
+ // not via safe_write. Entry required here because the MCP tool availability guard boilerplate
3930
+ // in its system prompt mentions stackwright_pro_safe_write (triggering the coherence test).
3931
+ "stackwright-pro-domain-expert-otter": [],
3566
3932
  "stackwright-services-otter": [
3567
3933
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Services config artifact" },
3568
3934
  { prefix: "services/", suffix: ".ts", description: "Service implementation files" },
@@ -3882,10 +4248,10 @@ function registerSafeWriteTools(server2) {
3882
4248
  "stackwright_pro_safe_write",
3883
4249
  DESC,
3884
4250
  {
3885
- callerOtter: z12.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
3886
- filePath: z12.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
3887
- content: z12.string().describe("File content to write"),
3888
- createDirectories: boolCoerce(z12.boolean().optional().default(true)).describe(
4251
+ callerOtter: z14.string().describe('The otter agent name requesting the write, e.g. "stackwright-pro-page-otter"'),
4252
+ filePath: z14.string().describe('Relative path from project root, e.g. "pages/dashboard/content.yml"'),
4253
+ content: z14.string().describe("File content to write"),
4254
+ createDirectories: boolCoerce(z14.boolean().optional().default(true)).describe(
3889
4255
  "Create parent directories if they don't exist. Default: true"
3890
4256
  )
3891
4257
  },
@@ -3902,7 +4268,7 @@ function registerSafeWriteTools(server2) {
3902
4268
  }
3903
4269
 
3904
4270
  // src/tools/auth.ts
3905
- import { z as z13 } from "zod";
4271
+ import { z as z15 } from "zod";
3906
4272
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "fs";
3907
4273
  import { join as join6 } from "path";
3908
4274
  function buildHierarchy(roles) {
@@ -4774,38 +5140,38 @@ function registerAuthTools(server2) {
4774
5140
  "stackwright_pro_configure_auth",
4775
5141
  "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.",
4776
5142
  {
4777
- method: z13.enum(["cac", "oidc", "oauth2", "none"]),
4778
- provider: z13.enum(["azure-ad", "okta", "ping", "cognito", "custom"]).optional(),
5143
+ method: z15.enum(["cac", "oidc", "oauth2", "none"]),
5144
+ provider: z15.enum(["azure-ad", "okta", "ping", "cognito", "custom"]).optional(),
4779
5145
  // CAC
4780
- cacCaBundle: z13.string().optional(),
4781
- cacEdipiLookup: z13.string().optional(),
4782
- cacOcspEndpoint: z13.string().optional(),
4783
- cacCertHeader: z13.string().optional(),
5146
+ cacCaBundle: z15.string().optional(),
5147
+ cacEdipiLookup: z15.string().optional(),
5148
+ cacOcspEndpoint: z15.string().optional(),
5149
+ cacCertHeader: z15.string().optional(),
4784
5150
  // OIDC
4785
- oidcDiscoveryUrl: z13.string().optional(),
4786
- oidcClientId: z13.string().optional(),
4787
- oidcClientSecret: z13.string().optional(),
4788
- oidcScopes: z13.string().optional(),
4789
- oidcRoleClaim: z13.string().optional(),
5151
+ oidcDiscoveryUrl: z15.string().optional(),
5152
+ oidcClientId: z15.string().optional(),
5153
+ oidcClientSecret: z15.string().optional(),
5154
+ oidcScopes: z15.string().optional(),
5155
+ oidcRoleClaim: z15.string().optional(),
4790
5156
  // OAuth2
4791
- oauth2AuthUrl: z13.string().optional(),
4792
- oauth2TokenUrl: z13.string().optional(),
4793
- oauth2ClientId: z13.string().optional(),
4794
- oauth2ClientSecret: z13.string().optional(),
4795
- oauth2Scopes: z13.string().optional(),
5157
+ oauth2AuthUrl: z15.string().optional(),
5158
+ oauth2TokenUrl: z15.string().optional(),
5159
+ oauth2ClientId: z15.string().optional(),
5160
+ oauth2ClientSecret: z15.string().optional(),
5161
+ oauth2Scopes: z15.string().optional(),
4796
5162
  // RBAC
4797
- rbacRoles: jsonCoerce(z13.array(z13.string()).optional()),
4798
- rbacDefaultRole: z13.string().optional(),
5163
+ rbacRoles: jsonCoerce(z15.array(z15.string()).optional()),
5164
+ rbacDefaultRole: z15.string().optional(),
4799
5165
  // Audit
4800
- auditEnabled: boolCoerce(z13.boolean().optional()),
4801
- auditRetentionDays: numCoerce(z13.number().int().positive().optional()),
5166
+ auditEnabled: boolCoerce(z15.boolean().optional()),
5167
+ auditRetentionDays: numCoerce(z15.number().int().positive().optional()),
4802
5168
  // Routes
4803
- protectedRoutes: jsonCoerce(z13.array(z13.string()).optional()),
5169
+ protectedRoutes: jsonCoerce(z15.array(z15.string()).optional()),
4804
5170
  // Injection for tests
4805
- _cwd: z13.string().optional(),
4806
- devOnly: boolCoerce(z13.boolean().optional()),
4807
- mockUsers: jsonCoerce(z13.array(z13.object({ name: z13.string(), email: z13.string() })).optional()),
4808
- nextMajorVersion: numCoerce(z13.number().int().positive().optional())
5171
+ _cwd: z15.string().optional(),
5172
+ devOnly: boolCoerce(z15.boolean().optional()),
5173
+ mockUsers: jsonCoerce(z15.array(z15.object({ name: z15.string(), email: z15.string() })).optional()),
5174
+ nextMajorVersion: numCoerce(z15.number().int().positive().optional())
4809
5175
  },
4810
5176
  async (params) => {
4811
5177
  const cwd = params._cwd ?? process.cwd();
@@ -4821,59 +5187,59 @@ import { join as join7, basename } from "path";
4821
5187
  var _checksums = /* @__PURE__ */ new Map([
4822
5188
  [
4823
5189
  "stackwright-pro-api-otter.json",
4824
- "1b9fea59a3e5b1e1015229a4e8d1231b6b87e1d12f23a81ecbbd79fe8657a2e0"
5190
+ "df79f4389a576c2885efa07b04f613c60eb8ebf4a8b1d4c7da5e4bb6ee4248dd"
4825
5191
  ],
4826
5192
  [
4827
5193
  "stackwright-pro-auth-otter.json",
4828
- "f91f82468da9c273fbe6481a3a40957d4c7aa3fa72528c492b6ac5d8e06a563a"
5194
+ "539c7145e88dd2afa7f2417be8cfa8e0e7d82608d051271a20f9c7e4e77d98e7"
4829
5195
  ],
4830
5196
  [
4831
5197
  "stackwright-pro-dashboard-otter.json",
4832
- "9c319d311801730e8dc9bc142eebb8fc5a7f48da48fa0b8d8c3b7431652447be"
5198
+ "160af221e04200f53709f8c3e249ca6dafb38a325b232fabd478b28f5492ab01"
4833
5199
  ],
4834
5200
  [
4835
5201
  "stackwright-pro-data-otter.json",
4836
- "f39061981caf0bd14e4eb83e74ac200450425536d14b8c00d60be94759a1ca9d"
5202
+ "b135cb0013edaa40baaf8a03f1d8795920dc0ce611183f047e55104bf8cf35be"
4837
5203
  ],
4838
5204
  [
4839
5205
  "stackwright-pro-designer-otter.json",
4840
- "af09ac8f06385bdbac63e2820daa2ff7d38b8ff1ff383c161f07e3fb9d9359c5"
5206
+ "1364b2c235c07b0b798e9aab90a68604f60019a5508d41ba576977f173e20cd9"
4841
5207
  ],
4842
5208
  [
4843
5209
  "stackwright-pro-domain-expert-otter.json",
4844
- "41e3a5838f05f6a51ed272860dd2d5b1df1f20bfc847eb8f39be109b89738e99"
5210
+ "1f21b8ff3450bdae29a4d31b31462ba22583995a448af3c11ab0a5071f46bd72"
4845
5211
  ],
4846
5212
  [
4847
5213
  "stackwright-pro-foreman-otter.json",
4848
- "4948b8189223e4ced81b632cca7d14b79fde85f204483ab8e847182692c4ee57"
5214
+ "438b03d2c35f64536055c68b3a9044fe3b5e4f2889acde4500dd801fad724be1"
4849
5215
  ],
4850
5216
  [
4851
5217
  "stackwright-pro-geo-otter.json",
4852
- "ff1191af8108cc70c09afe6a99b008dcb46f71e20f884da1ee424e431868a1b6"
5218
+ "2ec83c26a08c413d9553ff8b8f0f0f643c1a8da043741b356e27106cad78f05f"
4853
5219
  ],
4854
5220
  [
4855
5221
  "stackwright-pro-page-otter.json",
4856
- "cdd878857c1d809c60263693ab0c6cbb45087fd9c3eeda5b4628bd7026d2bded"
5222
+ "0057ea97f7c2e33a8673140f59a0237eb627d82c676af3fae4faa31033598e03"
4857
5223
  ],
4858
5224
  [
4859
5225
  "stackwright-pro-polish-otter.json",
4860
- "e5f88d054dd536646f48e5d47cbd64f825f493100002309b90c912fa69ef279e"
5226
+ "02cfa56354bfcd33af6d634ac647f38812a2353e487816b67edc2e9eb2c3e357"
4861
5227
  ],
4862
5228
  [
4863
5229
  "stackwright-pro-scaffold-otter.json",
4864
- "c9950a34cc6a729a2ad22a4052afc3d7f9b033fe390d9edba4014b76e1d076b6"
5230
+ "ccbc9d20a65dcf7cc2eacb5ac3060cd95a10a3ef795883170684658872299e7b"
4865
5231
  ],
4866
5232
  [
4867
5233
  "stackwright-pro-theme-otter.json",
4868
- "532d9ca7db6911a09ce5029a8c74c89360bdf6cb8ede8c2636866b509cbe06f9"
5234
+ "a271eac375db9d014afb781536f60f2e6716a9054b12b9c35713af09a63912ff"
4869
5235
  ],
4870
5236
  [
4871
5237
  "stackwright-pro-workflow-otter.json",
4872
- "80eb6c8d3f079e00533dcb4a3c7fd6baae4d5f4192ab6c27c85d2de5359592fd"
5238
+ "6cc800374de6e723a283e4757af97f7b8337c64ee78053d7e50a79e17314a049"
4873
5239
  ],
4874
5240
  [
4875
5241
  "stackwright-services-otter.json",
4876
- "b8a252d6a2c5090138899925d3ea95bf3339b6e49adc276a1f5f7304b4ae5134"
5242
+ "c013d7fc2475e62d0af54d57bc988182feee7766bac0edf841cbfad5c73e9261"
4877
5243
  ]
4878
5244
  ]);
4879
5245
  Object.freeze(_checksums);
@@ -5089,7 +5455,7 @@ function registerIntegrityTools(server2) {
5089
5455
  }
5090
5456
 
5091
5457
  // src/tools/domain.ts
5092
- import { z as z14 } from "zod";
5458
+ import { z as z16 } from "zod";
5093
5459
  import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
5094
5460
  import { join as join8 } from "path";
5095
5461
  function handleListCollections(input) {
@@ -5475,7 +5841,7 @@ function registerDomainTools(server2) {
5475
5841
  "stackwright_pro_resolve_data_strategy",
5476
5842
  "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.",
5477
5843
  {
5478
- strategy: z14.string().describe(
5844
+ strategy: z16.string().describe(
5479
5845
  'The data-1 answer value: "pulse-fast", "isr-fast", "isr-standard", or "isr-slow"'
5480
5846
  )
5481
5847
  },
@@ -5485,7 +5851,7 @@ function registerDomainTools(server2) {
5485
5851
  "stackwright_pro_validate_workflow",
5486
5852
  "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.",
5487
5853
  {
5488
- workflow: jsonCoerce(z14.record(z14.string(), z14.unknown()).optional()).describe(
5854
+ workflow: jsonCoerce(z16.record(z16.string(), z16.unknown()).optional()).describe(
5489
5855
  "Parsed workflow object. If omitted, reads from .stackwright/artifacts/workflow-config.json"
5490
5856
  )
5491
5857
  },
@@ -5494,7 +5860,7 @@ function registerDomainTools(server2) {
5494
5860
  }
5495
5861
 
5496
5862
  // src/tools/type-schemas.ts
5497
- import { z as z15 } from "zod";
5863
+ import { z as z17 } from "zod";
5498
5864
  function buildTypeSchemaSummary() {
5499
5865
  return {
5500
5866
  version: "1.0",
@@ -5571,7 +5937,7 @@ function registerTypeSchemasTool(server2) {
5571
5937
  "stackwright_pro_get_type_schemas",
5572
5938
  "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.",
5573
5939
  {
5574
- format: z15.enum(["full", "domains-only"]).optional().default("full").describe("full = complete summary with all fields; domains-only = just domain names")
5940
+ format: z17.enum(["full", "domains-only"]).optional().default("full").describe("full = complete summary with all fields; domains-only = just domain names")
5575
5941
  },
5576
5942
  async ({ format }) => {
5577
5943
  const summary = buildTypeSchemaSummary();
@@ -5769,7 +6135,7 @@ function registerScaffoldCleanupTools(server2) {
5769
6135
  }
5770
6136
 
5771
6137
  // src/tools/contrast.ts
5772
- import { z as z16 } from "zod";
6138
+ import { z as z18 } from "zod";
5773
6139
  function linearizeChannel(c255) {
5774
6140
  const c = c255 / 255;
5775
6141
  return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
@@ -5941,8 +6307,8 @@ function registerContrastTools(server2) {
5941
6307
  "stackwright_pro_check_contrast",
5942
6308
  '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%").',
5943
6309
  {
5944
- fg: z16.string().describe("Foreground (text) color \u2014 hex or HSL"),
5945
- bg: z16.string().describe("Background color \u2014 hex or HSL")
6310
+ fg: z18.string().describe("Foreground (text) color \u2014 hex or HSL"),
6311
+ bg: z18.string().describe("Background color \u2014 hex or HSL")
5946
6312
  },
5947
6313
  async ({ fg, bg }) => {
5948
6314
  let result;
@@ -5972,8 +6338,8 @@ function registerContrastTools(server2) {
5972
6338
  "stackwright_pro_derive_accessible_palette",
5973
6339
  '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%").',
5974
6340
  {
5975
- seed: z16.string().describe("Background (seed) color \u2014 hex or HSL"),
5976
- targetRatio: numCoerce(z16.number().default(4.5)).describe(
6341
+ seed: z18.string().describe("Background (seed) color \u2014 hex or HSL"),
6342
+ targetRatio: numCoerce(z18.number().default(4.5)).describe(
5977
6343
  "Minimum required contrast ratio (default 4.5 for WCAG AA)"
5978
6344
  )
5979
6345
  },
@@ -6010,9 +6376,11 @@ function registerContrastTools(server2) {
6010
6376
  // package.json
6011
6377
  var package_default = {
6012
6378
  dependencies: {
6013
- "@stackwright-pro/types": "workspace:*",
6014
6379
  "@modelcontextprotocol/sdk": "^1.10.0",
6015
6380
  "@stackwright-pro/cli-data-explorer": "workspace:*",
6381
+ "@stackwright-pro/types": "workspace:*",
6382
+ "@types/js-yaml": "^4.0.9",
6383
+ "js-yaml": "^4.2.0",
6016
6384
  zod: "^4.4.3"
6017
6385
  },
6018
6386
  devDependencies: {
@@ -6030,7 +6398,7 @@ var package_default = {
6030
6398
  "test:coverage": "vitest run --coverage"
6031
6399
  },
6032
6400
  name: "@stackwright-pro/mcp",
6033
- version: "0.2.0-alpha.80",
6401
+ version: "0.2.0-alpha.82",
6034
6402
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
6035
6403
  license: "SEE LICENSE IN LICENSE",
6036
6404
  main: "./dist/server.js",
@@ -6084,6 +6452,8 @@ registerIntegrityTools(server);
6084
6452
  registerArtifactSigningTools(server);
6085
6453
  registerDomainTools(server);
6086
6454
  registerTypeSchemasTool(server);
6455
+ registerValidateYamlFragmentTool(server);
6456
+ registerGetSchemaTool(server);
6087
6457
  registerScaffoldCleanupTools(server);
6088
6458
  registerContrastTools(server);
6089
6459
  async function main() {