@stackwright-pro/mcp 0.2.0-alpha.60 → 0.2.0-alpha.66

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
@@ -1702,6 +1702,14 @@ function handleSavePhaseAnswers(input) {
1702
1702
  let answers;
1703
1703
  if (input.questions && input.questions.length > 0) {
1704
1704
  answers = answersToManifestFormat(input.rawAnswers, input.questions);
1705
+ if (Object.keys(answers).length === 0 && input.rawAnswers.length > 0) {
1706
+ answers = Object.fromEntries(
1707
+ input.rawAnswers.map((a) => [
1708
+ a.question_header,
1709
+ a.selected_options.length > 1 ? a.selected_options : a.selected_options[0] ?? ""
1710
+ ])
1711
+ );
1712
+ }
1705
1713
  } else {
1706
1714
  answers = Object.fromEntries(
1707
1715
  input.rawAnswers.map((a) => [a.question_header, a.selected_options[0] ?? ""])
@@ -2285,12 +2293,13 @@ var PHASE_DEPENDENCIES = {
2285
2293
  // workflow states as trigger sources. Produces OpenAPI specs consumed
2286
2294
  // by pages and dashboard for typed data wiring.
2287
2295
  services: ["api", "workflow"],
2288
- // pages/dashboard: now also depend on services for typed endpoint wiring
2289
- // via the OpenAPI specs that services produces.
2290
- // 'api' is still transitive through 'data'; auth removed page-otter has
2291
- // graceful fallback and reads role names from workflow artifacts instead
2292
- pages: ["designer", "theme", "data", "services"],
2293
- dashboard: ["designer", "theme", "data", "services"],
2296
+ // pages/dashboard: depend on services for typed endpoint wiring (OpenAPI
2297
+ // specs) and on geo so the specialist prompt includes geo-manifest.json
2298
+ // page/dashboard otters see which page slugs are already claimed and
2299
+ // won't attempt to overwrite them (swp-73c). 'api' is still transitive
2300
+ // through 'data'; auth removed — page-otter has graceful fallback.
2301
+ pages: ["designer", "theme", "data", "services", "geo"],
2302
+ dashboard: ["designer", "theme", "data", "services", "geo"],
2294
2303
  // auth is the penultimate phase — runs after all content-producing phases
2295
2304
  // so it can read pages, dashboard, workflow, and geo artifacts for route
2296
2305
  // protection and RBAC wiring. Skipped upstream phases still satisfy deps
@@ -3032,13 +3041,19 @@ var PHASE_ARTIFACT_SCHEMA = {
3032
3041
  null,
3033
3042
  2
3034
3043
  ),
3044
+ // type: 'pki' = CAC/DoD certificate auth | 'oidc' = enterprise SSO
3045
+ // For dev-only mock auth: use type: 'oidc' with devOnly: true (Zod strips devOnly — it's a convention only)
3035
3046
  auth: JSON.stringify(
3036
3047
  {
3037
3048
  version: "1.0",
3038
3049
  generatedBy: "stackwright-pro-auth-otter",
3039
3050
  authConfig: {
3040
- method: "<cac|oidc|oauth2|none>",
3041
- provider: "<azure-ad|okta|ping|cognito \u2014 if OIDC, else null>",
3051
+ type: "<pki|oidc>",
3052
+ // OIDC-only fields (omit for pki):
3053
+ provider: "<azure_ad|okta|cognito|auth0|authentik|keycloak|custom>",
3054
+ discoveryUrl: "<IdP OIDC discovery URL>",
3055
+ clientId: "<OIDC client ID>",
3056
+ clientSecret: "<OIDC client secret>",
3042
3057
  rbacRoles: ["ADMIN", "ANALYST"],
3043
3058
  rbacDefaultRole: "ANALYST",
3044
3059
  protectedRoutes: ["/dashboard/:path*", "/procurement/:path*"],
@@ -3341,6 +3356,16 @@ var OTTER_WRITE_ALLOWLISTS = {
3341
3356
  prefix: ".env",
3342
3357
  suffix: "",
3343
3358
  description: "Dotenv files (.env, .env.local, .env.production, etc.)"
3359
+ },
3360
+ {
3361
+ prefix: "lib/mock-auth",
3362
+ suffix: ".ts",
3363
+ description: "Mock auth module (DEV_ONLY_MODE role updates)"
3364
+ },
3365
+ {
3366
+ prefix: "package.json",
3367
+ suffix: ".json",
3368
+ description: "Project package.json (DEV_ONLY_MODE script cleanup)"
3344
3369
  }
3345
3370
  ],
3346
3371
  "stackwright-pro-data-otter": [
@@ -3379,6 +3404,16 @@ var OTTER_WRITE_ALLOWLISTS = {
3379
3404
  { prefix: "pages/", suffix: "/content.yml", description: "Landing page content" },
3380
3405
  { prefix: "pages/", suffix: "/content.yaml", description: "Landing page content" },
3381
3406
  { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Polish artifact" }
3407
+ ],
3408
+ "stackwright-services-otter": [
3409
+ { prefix: ".stackwright/artifacts/", suffix: ".json", description: "Services config artifact" },
3410
+ { prefix: "services/", suffix: ".ts", description: "Service implementation files" },
3411
+ { prefix: "services/", suffix: ".yaml", description: "Service flow definitions" },
3412
+ { prefix: "services/", suffix: ".yml", description: "Service flow definitions" },
3413
+ { prefix: "lib/seeds/", suffix: ".ts", description: "Seed data files" },
3414
+ { prefix: "specs/", suffix: ".json", description: "Generated OpenAPI specs" },
3415
+ { prefix: "specs/", suffix: ".yaml", description: "Generated OpenAPI specs" },
3416
+ { prefix: "stackwright-generated/", suffix: ".json", description: "Services manifest" }
3382
3417
  ]
3383
3418
  };
3384
3419
  var PROTECTED_PATH_PREFIXES = [
@@ -3388,7 +3423,9 @@ var PROTECTED_PATH_PREFIXES = [
3388
3423
  ".stackwright/artifacts/signatures.json",
3389
3424
  // artifact signature manifest
3390
3425
  ".stackwright/questions/",
3391
- ".stackwright/answers/"
3426
+ ".stackwright/answers/",
3427
+ ".stackwright/page-registry.json"
3428
+ // page ownership — managed by safe_write internally
3392
3429
  ];
3393
3430
  var MAX_SAFE_WRITE_BYTES_JSON = 512 * 1024;
3394
3431
  var MAX_SAFE_WRITE_BYTES_YAML = 256 * 1024;
@@ -3402,6 +3439,58 @@ function getMaxBytesForPath(filePath) {
3402
3439
  return { limit: MAX_SAFE_WRITE_BYTES_ENV, label: "env" };
3403
3440
  return { limit: MAX_SAFE_WRITE_BYTES_DEFAULT, label: "default" };
3404
3441
  }
3442
+ var PAGE_REGISTRY_FILE = ".stackwright/page-registry.json";
3443
+ function extractPageSlug(filePath) {
3444
+ const match = (0, import_path6.normalize)(filePath).match(/^pages\/(.+?)\/content\.ya?ml$/);
3445
+ return match?.[1] ?? null;
3446
+ }
3447
+ function extractContentTypes(yamlContent) {
3448
+ const typePattern = /^\s*-?\s*type:\s*(\S+)/gm;
3449
+ const types = [];
3450
+ let m;
3451
+ while ((m = typePattern.exec(yamlContent)) !== null) {
3452
+ const typeName = m[1];
3453
+ if (typeName && !types.includes(typeName)) {
3454
+ types.push(typeName);
3455
+ }
3456
+ }
3457
+ return types.length > 0 ? types : ["unknown"];
3458
+ }
3459
+ function readPageRegistry(cwd) {
3460
+ const regPath = (0, import_path6.join)(cwd, PAGE_REGISTRY_FILE);
3461
+ if (!(0, import_fs6.existsSync)(regPath)) return {};
3462
+ try {
3463
+ const stat = (0, import_fs6.lstatSync)(regPath);
3464
+ if (stat.isSymbolicLink()) return {};
3465
+ return JSON.parse((0, import_fs6.readFileSync)(regPath, "utf-8"));
3466
+ } catch {
3467
+ return {};
3468
+ }
3469
+ }
3470
+ function writePageRegistry(cwd, registry) {
3471
+ const dir = (0, import_path6.join)(cwd, ".stackwright");
3472
+ (0, import_fs6.mkdirSync)(dir, { recursive: true });
3473
+ const regPath = (0, import_path6.join)(cwd, PAGE_REGISTRY_FILE);
3474
+ if ((0, import_fs6.existsSync)(regPath)) {
3475
+ const stat = (0, import_fs6.lstatSync)(regPath);
3476
+ if (stat.isSymbolicLink()) {
3477
+ throw new Error("Refusing to write page registry through symlink");
3478
+ }
3479
+ }
3480
+ (0, import_fs6.writeFileSync)(regPath, JSON.stringify(registry, null, 2) + "\n", { encoding: "utf-8" });
3481
+ }
3482
+ function checkPageOwnership(cwd, slug, callerOtter) {
3483
+ const registry = readPageRegistry(cwd);
3484
+ const existing = registry[slug];
3485
+ if (!existing || existing.claimedBy === callerOtter) {
3486
+ return { allowed: true };
3487
+ }
3488
+ return {
3489
+ allowed: false,
3490
+ owner: existing.claimedBy,
3491
+ contentTypes: existing.contentTypes
3492
+ };
3493
+ }
3405
3494
  function checkPathAllowed(callerOtter, filePath) {
3406
3495
  const normalized = (0, import_path6.normalize)(filePath);
3407
3496
  if (normalized.includes("..")) {
@@ -3443,6 +3532,16 @@ function checkPathAllowed(callerOtter, filePath) {
3443
3532
  continue;
3444
3533
  }
3445
3534
  }
3535
+ if (rule.prefix === "lib/mock-auth" && rule.suffix === ".ts") {
3536
+ if (normalized !== "lib/mock-auth.ts") {
3537
+ continue;
3538
+ }
3539
+ }
3540
+ if (rule.prefix === "package.json" && rule.suffix === ".json") {
3541
+ if (normalized !== "package.json") {
3542
+ continue;
3543
+ }
3544
+ }
3446
3545
  return { allowed: true, rule: rule.description };
3447
3546
  }
3448
3547
  }
@@ -3568,11 +3667,38 @@ function handleSafeWrite(input) {
3568
3667
  };
3569
3668
  return { text: JSON.stringify(result), isError: true };
3570
3669
  }
3670
+ const pageSlug = extractPageSlug(normalized);
3671
+ if (pageSlug) {
3672
+ const ownership = checkPageOwnership(cwd, pageSlug, callerOtter);
3673
+ if (!ownership.allowed) {
3674
+ const result = {
3675
+ success: false,
3676
+ error: `Page slug "${pageSlug}" is already claimed by ${ownership.owner} (content types: ${ownership.contentTypes.join(", ")}). Use a different slug or coordinate with the owning otter.`,
3677
+ callerOtter,
3678
+ attemptedPath: filePath,
3679
+ allowedPaths: []
3680
+ };
3681
+ return { text: JSON.stringify(result), isError: true };
3682
+ }
3683
+ }
3571
3684
  try {
3572
3685
  if (createDirectories) {
3573
3686
  (0, import_fs6.mkdirSync)((0, import_path6.dirname)(fullPath), { recursive: true });
3574
3687
  }
3575
3688
  (0, import_fs6.writeFileSync)(fullPath, content, { encoding: "utf-8" });
3689
+ if (pageSlug) {
3690
+ try {
3691
+ const registry = readPageRegistry(cwd);
3692
+ registry[pageSlug] = {
3693
+ slug: pageSlug,
3694
+ claimedBy: callerOtter,
3695
+ contentTypes: extractContentTypes(content),
3696
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString()
3697
+ };
3698
+ writePageRegistry(cwd, registry);
3699
+ } catch {
3700
+ }
3701
+ }
3576
3702
  const result = {
3577
3703
  success: true,
3578
3704
  path: normalized,
@@ -3839,10 +3965,232 @@ ${routeLines}
3839
3965
  ${auditSection}
3840
3966
  `;
3841
3967
  }
3968
+ function generateDevOnlyMiddlewareContent(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes) {
3969
+ const rbacBlock = ` rbac: {
3970
+ roles: ${JSON.stringify(roles)},
3971
+ defaultRole: '${defaultRole}',
3972
+ hierarchy: ${JSON.stringify(hierarchy, null, 4)},
3973
+ },`;
3974
+ const auditBlock = ` audit: {
3975
+ enabled: ${auditEnabled},
3976
+ retentionDays: ${auditRetentionDays},
3977
+ },`;
3978
+ const routesBlock = ` protectedRoutes: ${JSON.stringify(protectedRoutes)},`;
3979
+ const configBlock = `export const config = {
3980
+ matcher: ${JSON.stringify(protectedRoutes)},
3981
+ };`;
3982
+ const devHeader = [
3983
+ "// middleware.ts \u2014 generated by @stackwright-pro/auth-nextjs (dev-only mock)",
3984
+ "// DEV ONLY \u2014 uses mock authentication from lib/mock-auth.ts",
3985
+ "// Do NOT deploy to production.",
3986
+ "import { createProMiddleware } from '@stackwright-pro/auth-nextjs';",
3987
+ "import { mockAuthProvider } from './lib/mock-auth';"
3988
+ ].join("\n");
3989
+ if (method === "cac") {
3990
+ const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
3991
+ const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
3992
+ const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
3993
+ const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
3994
+ return `${devHeader}
3995
+
3996
+ export const middleware = createProMiddleware({
3997
+ method: 'cac',
3998
+ cac: {
3999
+ caBundle: '${caBundle}',
4000
+ edipiLookup: '${edipiLookup}',
4001
+ ocspEndpoint: '${ocspEndpoint}',
4002
+ certHeader: '${certHeader}',
4003
+ provider: mockAuthProvider,
4004
+ },
4005
+ ${rbacBlock}
4006
+ ${auditBlock}
4007
+ ${routesBlock}
4008
+ });
4009
+
4010
+ ${configBlock}
4011
+ `;
4012
+ }
4013
+ if (method === "oidc") {
4014
+ const scopes2 = params.oidcScopes ?? "openid profile email";
4015
+ const roleClaim = params.oidcRoleClaim ?? "roles";
4016
+ return `${devHeader}
4017
+
4018
+ export const middleware = createProMiddleware({
4019
+ method: 'oidc',
4020
+ oidc: {
4021
+ discoveryUrl: 'https://dev-mock-oidc/.well-known/openid-configuration',
4022
+ clientId: 'dev-mock-client',
4023
+ clientSecret: 'dev-mock-secret',
4024
+ scopes: '${scopes2}',
4025
+ roleClaim: '${roleClaim}',
4026
+ provider: mockAuthProvider,
4027
+ },
4028
+ ${rbacBlock}
4029
+ ${auditBlock}
4030
+ ${routesBlock}
4031
+ });
4032
+
4033
+ ${configBlock}
4034
+ `;
4035
+ }
4036
+ const scopes = params.oauth2Scopes ?? "read write";
4037
+ return `${devHeader}
4038
+
4039
+ export const middleware = createProMiddleware({
4040
+ method: 'oauth2',
4041
+ oauth2: {
4042
+ authorizationUrl: 'https://dev-mock-oauth2/authorize',
4043
+ tokenUrl: 'https://dev-mock-oauth2/token',
4044
+ clientId: 'dev-mock-client',
4045
+ clientSecret: 'dev-mock-secret',
4046
+ scopes: '${scopes}',
4047
+ provider: mockAuthProvider,
4048
+ },
4049
+ ${rbacBlock}
4050
+ ${auditBlock}
4051
+ ${routesBlock}
4052
+ });
4053
+
4054
+ ${configBlock}
4055
+ `;
4056
+ }
4057
+ function generateDevOnlyYamlBlock(method, params, roles, defaultRole, hierarchy, auditEnabled, auditRetentionDays, protectedRoutes) {
4058
+ const rbacSection = ` rbac:
4059
+ roles:
4060
+ ${rolesToYaml(roles, " ")}
4061
+ defaultRole: ${defaultRole}
4062
+ hierarchy:
4063
+ ${hierarchyToYaml(hierarchy, " ")}`;
4064
+ const auditSection = ` audit:
4065
+ enabled: ${auditEnabled}
4066
+ retentionDays: ${auditRetentionDays}`;
4067
+ const routeLines = protectedRoutes.map((r) => ` - pattern: ${r}
4068
+ requiredRole: ${defaultRole}`).join("\n");
4069
+ const providerLine = params.provider ? ` provider: ${params.provider}
4070
+ ` : "";
4071
+ if (method === "cac") {
4072
+ const caBundle = params.cacCaBundle ?? "./certs/dod-ca-bundle.pem";
4073
+ const edipiLookup = params.cacEdipiLookup ?? "./config/edipi-lookup.json";
4074
+ const ocspEndpoint = params.cacOcspEndpoint ?? "https://ocsp.disa.mil";
4075
+ const certHeader = params.cacCertHeader ?? "X-SSL-Client-Cert";
4076
+ return `auth:
4077
+ method: cac
4078
+ devOnly: true
4079
+ ${providerLine} middleware: ./middleware.ts
4080
+ cac:
4081
+ caBundle: ${caBundle}
4082
+ edipiLookup: ${edipiLookup}
4083
+ ocspEndpoint: ${ocspEndpoint}
4084
+ certHeader: ${certHeader}
4085
+ ${rbacSection}
4086
+ protectedRoutes:
4087
+ ${routeLines}
4088
+ ${auditSection}
4089
+ `;
4090
+ }
4091
+ if (method === "oidc") {
4092
+ const scopes2 = params.oidcScopes ?? "openid profile email";
4093
+ const roleClaim = params.oidcRoleClaim ?? "roles";
4094
+ return `auth:
4095
+ method: oidc
4096
+ devOnly: true
4097
+ ${providerLine} middleware: ./middleware.ts
4098
+ oidc:
4099
+ discoveryUrl: https://dev-mock-oidc/.well-known/openid-configuration
4100
+ clientId: dev-mock-client
4101
+ clientSecret: dev-mock-secret
4102
+ scopes: ${scopes2}
4103
+ roleClaim: ${roleClaim}
4104
+ ${rbacSection}
4105
+ protectedRoutes:
4106
+ ${routeLines}
4107
+ ${auditSection}
4108
+ `;
4109
+ }
4110
+ const scopes = params.oauth2Scopes ?? "read write";
4111
+ return `auth:
4112
+ method: oauth2
4113
+ devOnly: true
4114
+ ${providerLine} middleware: ./middleware.ts
4115
+ oauth2:
4116
+ authorizationUrl: https://dev-mock-oauth2/authorize
4117
+ tokenUrl: https://dev-mock-oauth2/token
4118
+ clientId: dev-mock-client
4119
+ clientSecret: dev-mock-secret
4120
+ scopes: ${scopes}
4121
+ ${rbacSection}
4122
+ protectedRoutes:
4123
+ ${routeLines}
4124
+ ${auditSection}
4125
+ `;
4126
+ }
4127
+ function deriveDevKey(roleName) {
4128
+ const segment = roleName.split("_")[0];
4129
+ return (segment ?? roleName).toLowerCase();
4130
+ }
4131
+ function generateMockAuthContent(roles, mockUsers) {
4132
+ const entries = [];
4133
+ const scriptLines = [];
4134
+ for (let i = 0; i < roles.length; i++) {
4135
+ const role = roles[i];
4136
+ const devKey = deriveDevKey(role);
4137
+ const persona = mockUsers?.[i];
4138
+ const name = persona?.name ?? `Dev ${role}`;
4139
+ const email = persona?.email ?? `dev-${devKey}@example.mil`;
4140
+ const edipi = String(i + 1).padStart(10, "0");
4141
+ entries.push(` ${devKey}: {
4142
+ id: 'mock-${devKey}-${String(i + 1).padStart(3, "0")}',
4143
+ name: '${name}',
4144
+ email: '${email}',
4145
+ roles: ['${role}'],
4146
+ edipi: '${edipi}',
4147
+ }`);
4148
+ scriptLines.push(` * pnpm dev:${devKey} \u2014 ${name}`);
4149
+ }
4150
+ return `/**
4151
+ * Mock authentication for development mode.
4152
+ *
4153
+ * DEV_ONLY_MODE \u2014 no real auth provider. Select a persona via MOCK_USER env var
4154
+ * or use the convenience scripts in package.json:
4155
+ ${scriptLines.join("\n")}
4156
+ */
4157
+
4158
+ export const MOCK_USERS = {
4159
+ ${entries.join(",\n")}
4160
+ } as const;
4161
+
4162
+ export type MockRole = keyof typeof MOCK_USERS;
4163
+
4164
+ export function getMockUser(role?: string) {
4165
+ const key = (role ?? process.env.MOCK_USER) as MockRole | undefined;
4166
+ if (!key) return null;
4167
+ return MOCK_USERS[key] ?? null;
4168
+ }
4169
+
4170
+ export function mockAuthProvider() {
4171
+ return getMockUser();
4172
+ }
4173
+ `;
4174
+ }
4175
+ function updatePackageJsonScripts(existingJson, roles, mockUsers) {
4176
+ const pkg = JSON.parse(existingJson);
4177
+ const scripts = pkg.scripts ?? {};
4178
+ delete scripts["dev:admin"];
4179
+ delete scripts["dev:analyst"];
4180
+ delete scripts["dev:viewer"];
4181
+ for (let i = 0; i < roles.length; i++) {
4182
+ const devKey = deriveDevKey(roles[i]);
4183
+ scripts[`dev:${devKey}`] = `MOCK_USER=${devKey} next dev`;
4184
+ }
4185
+ pkg.scripts = scripts;
4186
+ return JSON.stringify(pkg, null, 2) + "\n";
4187
+ }
3842
4188
  async function configureAuthHandler(params, cwd) {
3843
4189
  const {
3844
4190
  method,
3845
4191
  provider,
4192
+ devOnly = false,
4193
+ mockUsers,
3846
4194
  rbacRoles = ["SUPER_ADMIN", "ADMIN", "ANALYST"],
3847
4195
  auditEnabled = true,
3848
4196
  auditRetentionDays = 90,
@@ -3872,7 +4220,16 @@ async function configureAuthHandler(params, cwd) {
3872
4220
  }
3873
4221
  const filesWritten = [];
3874
4222
  try {
3875
- const middlewareContent = generateMiddlewareContent(
4223
+ const middlewareContent = devOnly ? generateDevOnlyMiddlewareContent(
4224
+ method,
4225
+ params,
4226
+ roles,
4227
+ defaultRole,
4228
+ hierarchy,
4229
+ auditEnabled,
4230
+ auditRetentionDays,
4231
+ protectedRoutes
4232
+ ) : generateMiddlewareContent(
3876
4233
  method,
3877
4234
  params,
3878
4235
  roles,
@@ -3896,30 +4253,87 @@ async function configureAuthHandler(params, cwd) {
3896
4253
  isError: true
3897
4254
  };
3898
4255
  }
3899
- try {
3900
- const envBlock = generateEnvBlock(method, params);
3901
- const envPath = (0, import_path7.join)(cwd, ".env.example");
3902
- if ((0, import_fs7.existsSync)(envPath)) {
3903
- const existing = (0, import_fs7.readFileSync)(envPath, "utf8");
3904
- (0, import_fs7.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
3905
- } else {
3906
- (0, import_fs7.writeFileSync)(envPath, envBlock, "utf8");
4256
+ if (!devOnly) {
4257
+ try {
4258
+ const envBlock = generateEnvBlock(method, params);
4259
+ const envPath = (0, import_path7.join)(cwd, ".env.example");
4260
+ if ((0, import_fs7.existsSync)(envPath)) {
4261
+ const existing = (0, import_fs7.readFileSync)(envPath, "utf8");
4262
+ (0, import_fs7.writeFileSync)(envPath, existing.trimEnd() + "\n\n" + envBlock, "utf8");
4263
+ } else {
4264
+ (0, import_fs7.writeFileSync)(envPath, envBlock, "utf8");
4265
+ }
4266
+ filesWritten.push(".env.example");
4267
+ } catch (err) {
4268
+ const msg = err instanceof Error ? err.message : String(err);
4269
+ return {
4270
+ content: [
4271
+ {
4272
+ type: "text",
4273
+ text: JSON.stringify({ success: false, error: `Failed writing .env.example: ${msg}` })
4274
+ }
4275
+ ],
4276
+ isError: true
4277
+ };
4278
+ }
4279
+ }
4280
+ if (devOnly) {
4281
+ try {
4282
+ const mockAuthDir = (0, import_path7.join)(cwd, "lib");
4283
+ (0, import_fs7.mkdirSync)(mockAuthDir, { recursive: true });
4284
+ const mockAuthContent = generateMockAuthContent(roles, mockUsers);
4285
+ (0, import_fs7.writeFileSync)((0, import_path7.join)(mockAuthDir, "mock-auth.ts"), mockAuthContent, "utf8");
4286
+ filesWritten.push("lib/mock-auth.ts");
4287
+ } catch (err) {
4288
+ const msg = err instanceof Error ? err.message : String(err);
4289
+ return {
4290
+ content: [
4291
+ {
4292
+ type: "text",
4293
+ text: JSON.stringify({
4294
+ success: false,
4295
+ error: `Failed writing lib/mock-auth.ts: ${msg}`
4296
+ })
4297
+ }
4298
+ ],
4299
+ isError: true
4300
+ };
4301
+ }
4302
+ try {
4303
+ const pkgPath = (0, import_path7.join)(cwd, "package.json");
4304
+ if ((0, import_fs7.existsSync)(pkgPath)) {
4305
+ const existingPkg = (0, import_fs7.readFileSync)(pkgPath, "utf8");
4306
+ const updatedPkg = updatePackageJsonScripts(existingPkg, roles, mockUsers);
4307
+ (0, import_fs7.writeFileSync)(pkgPath, updatedPkg, "utf8");
4308
+ filesWritten.push("package.json");
4309
+ }
4310
+ } catch (err) {
4311
+ const msg = err instanceof Error ? err.message : String(err);
4312
+ return {
4313
+ content: [
4314
+ {
4315
+ type: "text",
4316
+ text: JSON.stringify({
4317
+ success: false,
4318
+ error: `Failed updating package.json: ${msg}`
4319
+ })
4320
+ }
4321
+ ],
4322
+ isError: true
4323
+ };
3907
4324
  }
3908
- filesWritten.push(".env.example");
3909
- } catch (err) {
3910
- const msg = err instanceof Error ? err.message : String(err);
3911
- return {
3912
- content: [
3913
- {
3914
- type: "text",
3915
- text: JSON.stringify({ success: false, error: `Failed writing .env.example: ${msg}` })
3916
- }
3917
- ],
3918
- isError: true
3919
- };
3920
4325
  }
3921
4326
  try {
3922
- const authYaml = generateYamlBlock(
4327
+ const authYaml = devOnly ? generateDevOnlyYamlBlock(
4328
+ method,
4329
+ params,
4330
+ roles,
4331
+ defaultRole,
4332
+ hierarchy,
4333
+ auditEnabled,
4334
+ auditRetentionDays,
4335
+ protectedRoutes
4336
+ ) : generateYamlBlock(
3923
4337
  method,
3924
4338
  params,
3925
4339
  roles,
@@ -4001,7 +4415,9 @@ function registerAuthTools(server2) {
4001
4415
  // Routes
4002
4416
  protectedRoutes: jsonCoerce(import_zod13.z.array(import_zod13.z.string()).optional()),
4003
4417
  // Injection for tests
4004
- _cwd: import_zod13.z.string().optional()
4418
+ _cwd: import_zod13.z.string().optional(),
4419
+ devOnly: boolCoerce(import_zod13.z.boolean().optional()),
4420
+ mockUsers: jsonCoerce(import_zod13.z.array(import_zod13.z.object({ name: import_zod13.z.string(), email: import_zod13.z.string() })).optional())
4005
4421
  },
4006
4422
  async (params) => {
4007
4423
  const cwd = params._cwd ?? process.cwd();
@@ -4021,15 +4437,15 @@ var _checksums = /* @__PURE__ */ new Map([
4021
4437
  ],
4022
4438
  [
4023
4439
  "stackwright-pro-auth-otter.json",
4024
- "bf0e66e35d15ba818ba6ff1a007df34975565bacbb35cc0c80151fb1da13e573"
4440
+ "4bf6beba7150d08c74c5f6fbbeb20e988aba52a2029ff2892615e71f6ab12ed1"
4025
4441
  ],
4026
4442
  [
4027
4443
  "stackwright-pro-dashboard-otter.json",
4028
- "f5a83b74ad7c44edc6f39b45a568fa122d82aa4788f741ce14614da56d4e29a4"
4444
+ "9c319d311801730e8dc9bc142eebb8fc5a7f48da48fa0b8d8c3b7431652447be"
4029
4445
  ],
4030
4446
  [
4031
4447
  "stackwright-pro-data-otter.json",
4032
- "c406e1c775bcb1f2b038b40a92d9bd23172b40d774fc0fa50bad4c9714f53445"
4448
+ "4d9369277685a4acc484116920c9622ad8a1838012a493fcfe42a6ae5abe53cf"
4033
4449
  ],
4034
4450
  [
4035
4451
  "stackwright-pro-designer-otter.json",
@@ -4037,23 +4453,23 @@ var _checksums = /* @__PURE__ */ new Map([
4037
4453
  ],
4038
4454
  [
4039
4455
  "stackwright-pro-domain-expert-otter.json",
4040
- "bfe5c167d73fef3f2ef280fff56dcb552073c218e1394a43ecf983a03169ed55"
4456
+ "6055a2efc78f54a8393f628839e2a2563bf0c6de3ad32de00c82779a53381efd"
4041
4457
  ],
4042
4458
  [
4043
4459
  "stackwright-pro-foreman-otter.json",
4044
- "a3a4c6b3dde05d8bed213759b1b6644d345b3107b73624ff5654d30b98297649"
4460
+ "ab38ef53b95ec610a38b2866d78a135cbec16d257a9b35d7e46e2fee2d4de235"
4045
4461
  ],
4046
4462
  [
4047
4463
  "stackwright-pro-geo-otter.json",
4048
- "6eb7ecf97254dbd79c09ad24348bf16001423cce9585c14bef81afd67b7b901b"
4464
+ "9e09aaf2bb10197c6d1c05d0fd5f5f9380acc0cb697a410fcae839ffba648561"
4049
4465
  ],
4050
4466
  [
4051
4467
  "stackwright-pro-page-otter.json",
4052
- "9a5672f0758c81539337d86955e2892cd412547b4f111c2aa098eed1e62d7626"
4468
+ "532bb7e9a25a5c832edd1ff1ea0886dd4453905d86e6f9331eb957ae5e121833"
4053
4469
  ],
4054
4470
  [
4055
4471
  "stackwright-pro-polish-otter.json",
4056
- "d31116995fdb417798af6056efd03bb1c71e0891371aba1774d283c03c9d77e8"
4472
+ "8f284d4d6a204137cd786824fc584d5bddac1bc757204769b99ca5412cf2cea2"
4057
4473
  ],
4058
4474
  [
4059
4475
  "stackwright-pro-theme-otter.json",
@@ -4065,7 +4481,7 @@ var _checksums = /* @__PURE__ */ new Map([
4065
4481
  ],
4066
4482
  [
4067
4483
  "stackwright-services-otter.json",
4068
- "2a99df3e50415d027c0bc2a57f509882928bb1ae516e61dda667641ce1652ac3"
4484
+ "4893a596d187110124f78336ee91184a51b3c8d980c455382fe481adb9b487b5"
4069
4485
  ]
4070
4486
  ]);
4071
4487
  Object.freeze(_checksums);
@@ -4798,7 +5214,7 @@ var package_default = {
4798
5214
  "test:coverage": "vitest run --coverage"
4799
5215
  },
4800
5216
  name: "@stackwright-pro/mcp",
4801
- version: "0.2.0-alpha.60",
5217
+ version: "0.2.0-alpha.61",
4802
5218
  description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
4803
5219
  license: "SEE LICENSE IN LICENSE",
4804
5220
  main: "./dist/server.js",
@@ -4854,6 +5270,15 @@ registerDomainTools(server);
4854
5270
  registerTypeSchemasTool(server);
4855
5271
  async function main() {
4856
5272
  const transport = new import_stdio.StdioServerTransport();
5273
+ try {
5274
+ const servicesRegisterPkg = "@stackwright-services/mcp/register";
5275
+ const mod = await import(servicesRegisterPkg);
5276
+ if (typeof mod.registerServicesTools === "function") {
5277
+ mod.registerServicesTools(server);
5278
+ console.error("Stackwright Services tools registered on pro MCP");
5279
+ }
5280
+ } catch {
5281
+ }
4857
5282
  await server.connect(transport);
4858
5283
  console.error("Stackwright Pro MCP server running on stdio");
4859
5284
  }