@walkeros/mcp 4.7.0-next-1790722899482 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/stdio.js CHANGED
@@ -14,28 +14,52 @@ import { mcpResult, mcpError } from "@walkeros/core";
14
14
 
15
15
  // src/schemas/output.ts
16
16
  import { z } from "zod";
17
+ var ValidateIssueFields = {
18
+ path: z.string(),
19
+ message: z.string(),
20
+ value: z.unknown().optional(),
21
+ code: z.string().optional().describe("Stable code (the documented validate code list)"),
22
+ keyword: z.string().optional().describe("JSON Schema keyword of an ENTRY_SCHEMA finding")
23
+ };
17
24
  var ValidateOutputShape = {
18
25
  valid: z.boolean().describe("Whether validation passed"),
19
26
  type: z.union([
20
27
  z.enum(["contract", "entry", "event", "flow", "mapping"]),
21
28
  z.string().regex(/^(destinations|sources|transformers)\.\w+$/)
22
29
  ]).describe("What was validated"),
23
- errors: z.array(
24
- z.object({
25
- path: z.string(),
26
- message: z.string(),
27
- value: z.unknown().optional(),
28
- code: z.string().optional()
29
- })
30
- ).describe("Validation errors"),
30
+ errors: z.array(z.object(ValidateIssueFields)).describe("Validation errors"),
31
31
  warnings: z.array(
32
32
  z.object({
33
- path: z.string(),
34
- message: z.string(),
33
+ ...ValidateIssueFields,
35
34
  suggestion: z.string().optional()
36
35
  })
37
36
  ).describe("Validation warnings"),
38
- details: z.record(z.string(), z.unknown()).describe("Additional validation details")
37
+ details: z.object({
38
+ scope: z.object({
39
+ flows: z.array(z.string()),
40
+ entry: z.object({
41
+ section: z.string().optional(),
42
+ key: z.string(),
43
+ flows: z.array(z.string()),
44
+ searchedFlows: z.array(z.string())
45
+ }).optional(),
46
+ checks: z.array(z.string()),
47
+ offline: z.literal(true).optional()
48
+ }).optional().describe("What the run covered"),
49
+ skipped: z.array(
50
+ z.object({
51
+ path: z.string(),
52
+ check: z.string(),
53
+ reason: z.string(),
54
+ code: z.string()
55
+ })
56
+ ).optional().describe("Checks in scope that did not run, with the reason"),
57
+ deferred: z.array(z.object({ path: z.string(), reference: z.string() })).optional().describe("Values known only at runtime, not schema-checked")
58
+ }).catchall(z.unknown()).describe("Additional validation details"),
59
+ _hints: z.object({
60
+ next: z.array(z.string()).optional(),
61
+ warnings: z.array(z.string()).optional()
62
+ }).optional().describe("Suggested next steps")
39
63
  };
40
64
  var BundleOutputShape = {
41
65
  success: z.boolean().describe("Whether bundling succeeded"),
@@ -161,42 +185,14 @@ async function loadValidateInput(runtime, input, type) {
161
185
  throw error;
162
186
  }
163
187
  }
164
- var DEPRECATED_STORE_PACKAGE = "@walkeros/store-memory";
165
- function detectDeprecatedStorePackages(config) {
166
- const errors = [];
167
- if (!config || typeof config !== "object") return errors;
168
- const flows = config.flows;
169
- if (!flows || typeof flows !== "object") return errors;
170
- for (const [flowName, flowEntry] of Object.entries(
171
- flows
172
- )) {
173
- if (!flowEntry || typeof flowEntry !== "object") continue;
174
- const stores = flowEntry.stores;
175
- if (!stores || typeof stores !== "object") continue;
176
- for (const [storeId, storeEntry] of Object.entries(
177
- stores
178
- )) {
179
- if (!storeEntry || typeof storeEntry !== "object") continue;
180
- const pkg = storeEntry.package;
181
- if (pkg === DEPRECATED_STORE_PACKAGE) {
182
- errors.push({
183
- path: `flows.${flowName}.stores.${storeId}`,
184
- message: `Store "${storeId}" uses ${DEPRECATED_STORE_PACKAGE}, which has been removed. Use the built-in cache by omitting cache.store, or remove the store declaration if it was only used as a cache target.`,
185
- code: "DEPRECATED_PACKAGE"
186
- });
187
- }
188
- }
189
- }
190
- return errors;
191
- }
192
188
  var TITLE = "Validate Flow";
193
- var DESCRIPTION = "Validate walkerOS events, flow configurations, mapping rules, or data contracts. Accepts JSON strings, file paths, or URLs as input; on the hosted server only inline JSON or a saved flow id (flow_ or cfg_), no file paths or URLs. Returns validation results with errors, warnings, and details.";
189
+ var DESCRIPTION = "Validate walkerOS events, flow configurations, mapping rules, or data contracts. Accepts JSON strings, file paths, or URLs as input; on the hosted server only inline JSON or a saved flow id (flow_ or cfg_), no file paths or URLs. With only type and input, a flow runs every check on every flow and step, including each package's settings schema (fetched from the package CDN; offline: true skips that and says so). flow and path narrow the run; strict turns warnings and skips into failures: valid is false. Returns valid, errors and warnings with stable codes, and details.scope (what was checked), details.skipped (checks that could not run) and details.deferred (values known only at runtime).";
194
190
  var inputSchema = schemas.ValidateInputShape;
195
191
  var annotations = {
196
192
  readOnlyHint: true,
197
193
  destructiveHint: false,
198
194
  idempotentHint: true,
199
- openWorldHint: false
195
+ openWorldHint: true
200
196
  };
201
197
  function createFlowValidateToolSpec(runtime) {
202
198
  return {
@@ -209,41 +205,46 @@ function createFlowValidateToolSpec(runtime) {
209
205
  };
210
206
  }
211
207
  async function flowValidateHandlerBody(runtime, input) {
208
+ const parsed = schemas.ValidateInputSchema.safeParse(input ?? {});
209
+ if (!parsed.success) {
210
+ return mcpError(parsed.error, "Check the flow_validate input fields");
211
+ }
212
212
  const {
213
213
  type,
214
214
  input: validateInput,
215
215
  flow,
216
- path: path2
217
- } = input ?? {};
216
+ path: path3,
217
+ strict,
218
+ offline
219
+ } = parsed.data;
218
220
  try {
219
221
  const resolved = await loadValidateInput(runtime, validateInput, type);
222
+ const configDir = runtime.baseDir?.(validateInput);
220
223
  const result = await validate(type, resolved, {
221
224
  flow,
222
- path: path2
225
+ path: path3,
226
+ strict,
227
+ offline,
228
+ ...configDir ? { configDir } : {}
223
229
  });
224
- let augmented = result;
225
- if (type === "flow") {
226
- const deprecatedErrors = detectDeprecatedStorePackages(resolved);
227
- if (deprecatedErrors.length > 0) {
228
- augmented = {
229
- ...result,
230
- valid: false,
231
- errors: [...result.errors, ...deprecatedErrors]
232
- };
233
- }
234
- }
235
- const hints = augmented.valid ? {
236
- next: runtime.simulate ? [
237
- "Use flow_simulate to test event flow",
238
- "Use flow_bundle to build"
239
- ] : [HINT_OUT_OF_PROCESS]
240
- } : {
230
+ const skipped = result.details.skipped ?? [];
231
+ const next = runtime.simulate ? ["Use flow_simulate to test event flow", "Use flow_bundle to build"] : [HINT_OUT_OF_PROCESS];
232
+ const hints = !result.valid && result.errors.length === 0 ? {
233
+ next: [
234
+ `strict: ${result.warnings.length} warning(s) and ${skipped.length} skipped check(s) count as failures; resolve them, or run without strict`
235
+ ]
236
+ } : !result.valid ? {
241
237
  next: [
242
238
  "Fix errors above, then run flow_validate again",
243
239
  "Read walkeros://reference/flow-schema for correct structure"
244
240
  ]
245
- };
246
- return mcpResult(augmented, hints);
241
+ } : skipped.length > 0 ? {
242
+ next: [
243
+ `Valid in checked scope; ${skipped.length} check(s) skipped, see details.skipped`,
244
+ ...next
245
+ ]
246
+ } : { next };
247
+ return mcpResult(result, hints);
247
248
  } catch (error) {
248
249
  return mcpError(
249
250
  error,
@@ -266,15 +267,30 @@ function registerFlowValidateTool(server, runtime) {
266
267
  outputSchema: ValidateOutputShape,
267
268
  annotations: spec.annotations
268
269
  },
269
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
270
- // type-erased (input: unknown) => Promise<unknown> form by design.
271
- spec.handler
270
+ (args) => flowValidateHandlerBody(runtime, args)
272
271
  );
273
272
  }
274
273
 
275
274
  // src/tools/bundle.ts
276
275
  import { schemas as schemas2 } from "@walkeros/cli/dev";
277
- import { mcpResult as mcpResult2, mcpError as mcpError2 } from "@walkeros/core";
276
+ import { mcpResult as mcpResult2, mcpError as mcpError3 } from "@walkeros/core";
277
+
278
+ // src/tools/parse-input.ts
279
+ import { z as z2 } from "zod";
280
+ import { mcpError as mcpError2 } from "@walkeros/core";
281
+ function inputIssuesMessage(error) {
282
+ return error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ");
283
+ }
284
+ function parseToolInput(shape, raw) {
285
+ const parsed = z2.object(shape).safeParse(raw ?? {});
286
+ if (!parsed.success) {
287
+ return {
288
+ ok: false,
289
+ error: mcpError2(new Error(inputIssuesMessage(parsed.error)))
290
+ };
291
+ }
292
+ return { ok: true, data: parsed.data };
293
+ }
278
294
 
279
295
  // src/tools/resolve-config-path.ts
280
296
  async function resolveConfigPath(client, configPath) {
@@ -306,10 +322,12 @@ function createFlowBundleToolSpec(client, runtime) {
306
322
  };
307
323
  }
308
324
  async function flowBundleHandlerBody(client, runtime, input) {
309
- const { configPath, flow, stats, output } = input ?? {};
325
+ const parsed = parseToolInput(inputSchema2, input);
326
+ if (!parsed.ok) return parsed.error;
327
+ const { configPath, flow, stats, output } = parsed.data;
310
328
  if (!runtime.bundle) {
311
329
  const refusal = unavailableOperation("bundle");
312
- return mcpError2(refusal, refusal.hint);
330
+ return mcpError3(refusal, refusal.hint);
313
331
  }
314
332
  try {
315
333
  const resolvedConfigPath = await resolveConfigPath(client, configPath);
@@ -329,9 +347,8 @@ async function flowBundleHandlerBody(client, runtime, input) {
329
347
  }
330
348
  );
331
349
  }
332
- const output_ = result;
333
350
  return mcpResult2(
334
- { success: true, ...output_ },
351
+ { success: true, ...result },
335
352
  {
336
353
  next: [
337
354
  "Use flow_simulate to test",
@@ -340,7 +357,7 @@ async function flowBundleHandlerBody(client, runtime, input) {
340
357
  }
341
358
  );
342
359
  } catch (error) {
343
- return mcpError2(
360
+ return mcpError3(
344
361
  error,
345
362
  refusalHint(error, "Run flow_validate for detailed error messages")
346
363
  );
@@ -358,54 +375,39 @@ function registerFlowBundleTool(server, client, runtime) {
358
375
  outputSchema: BundleOutputShape,
359
376
  annotations: spec.annotations
360
377
  },
361
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
362
- // type-erased (input: unknown) => Promise<unknown> form by design.
363
- spec.handler
378
+ (args) => flowBundleHandlerBody(client, runtime, args)
364
379
  );
365
380
  }
366
381
 
367
382
  // src/tools/simulate.ts
368
- import { z as z2 } from "zod";
383
+ import { z as z3 } from "zod";
369
384
  import { schemas as schemas3 } from "@walkeros/cli/dev";
370
- import { isObject as isObject2, mcpResult as mcpResult3, mcpError as mcpError3 } from "@walkeros/core";
371
- import { scrubSecrets, toPrintable } from "@walkeros/core/node";
372
- import { maskKnownNumbers } from "@walkeros/cli";
385
+ import { filterValues, mcpResult as mcpResult4, mcpError as mcpError5 } from "@walkeros/core";
373
386
 
374
387
  // src/tools/simulate-description.ts
375
388
  var FLOW_SIMULATE_DESCRIPTION = `Simulate events through a walkerOS flow without making real API calls. For destinations: event is a walkerOS event { name: "entity action", data: {...} }. For sources: event is { content, trigger?: { type?, options? } }, where content is the walkerOS event { name: "entity action", data: {...} }. Only the simulated source starts. step (required) targets the step to simulate, e.g. "destination.gtag". Use flow_examples to discover available test data. A destination simulation starts only its target destination. A source's own walker commands (e.g. a CMP's walker consent) are returned as elb calls. Consent: state.consent is the collector consent the step starts from (any step; a consent-gated source such as the session source waits for it). A destination with require: ["consent"] waits until consent is present, so pass state.consent to start it; a destination with consent: { marketing: true } receives an event only when that consent is granted in state.consent or in the event's own consent. When a destination sends nothing, the result says why (waiting for its require, or a consent skip). command runs a step example with command (e.g. "consent") on a destination: event is the command's data, state.consent still applies first. Mapping transforms event names and data at the destination level. Policy redacts or injects fields before mapping runs.`;
376
389
 
377
- // src/tools/simulate.ts
378
- var STEP_TYPES = [
379
- "source",
380
- "transformer",
381
- "collector",
382
- "destination"
383
- ];
384
- function isStepType(value) {
385
- return STEP_TYPES.some((t) => t === value);
386
- }
390
+ // src/tools/egress.ts
391
+ import { isObject as isObject2, mcpResult as mcpResult3 } from "@walkeros/core";
392
+ import { scrubJson, scrubSecrets } from "@walkeros/core/node";
387
393
  function scrubbed(result, known) {
388
- const text = scrubSecrets(
389
- JSON.stringify(maskKnownNumbers(toPrintable(result), known)),
390
- { known }
391
- );
392
- let parsed;
393
- try {
394
- parsed = JSON.parse(text);
395
- } catch {
396
- parsed = void 0;
397
- }
398
- if (!isObject2(parsed))
399
- throw new Error("Simulation result could not be redacted as JSON.");
394
+ const parsed = parseObject(scrubJson(result, { known }));
395
+ if (!parsed) throw new Error("Result could not be redacted as JSON.");
400
396
  return parsed;
401
397
  }
402
398
  function scrubbedError(response, known) {
403
399
  let structuredContent;
404
400
  try {
405
- structuredContent = scrubbed(response.structuredContent, known);
401
+ if (response.structuredContent)
402
+ structuredContent = scrubbed(response.structuredContent, known);
406
403
  } catch {
407
- structuredContent = {
408
- error: "Simulation failed; the error could not be redacted."
404
+ structuredContent = void 0;
405
+ }
406
+ if (structuredContent) {
407
+ return {
408
+ ...response,
409
+ content: [{ type: "text", text: JSON.stringify(structuredContent) }],
410
+ structuredContent
409
411
  };
410
412
  }
411
413
  return {
@@ -414,9 +416,48 @@ function scrubbedError(response, known) {
414
416
  ...part,
415
417
  text: scrubSecrets(part.text, { known })
416
418
  })),
417
- structuredContent
419
+ structuredContent: { error: "The error could not be redacted." }
420
+ };
421
+ }
422
+ function scrubbedPushResult(result, known) {
423
+ const text = scrubJson(result, { known, space: 2 });
424
+ const parsed = parseObject(text);
425
+ if (parsed)
426
+ return mcpResult3({
427
+ ...parsed,
428
+ success: result.success,
429
+ duration: result.duration
430
+ });
431
+ return {
432
+ content: [{ type: "text", text }],
433
+ structuredContent: { success: result.success, duration: result.duration }
434
+ };
435
+ }
436
+ function parseObject(text) {
437
+ try {
438
+ const parsed = JSON.parse(text);
439
+ return isObject2(parsed) ? parsed : void 0;
440
+ } catch {
441
+ return void 0;
442
+ }
443
+ }
444
+ async function readRun(runtime, input) {
445
+ if (runtime.loadRun) return runtime.loadRun(input);
446
+ return {
447
+ knownSecrets: runtime.knownSecrets ? await runtime.knownSecrets(input) : []
418
448
  };
419
449
  }
450
+
451
+ // src/tools/simulate.ts
452
+ var STEP_TYPES = [
453
+ "source",
454
+ "transformer",
455
+ "collector",
456
+ "destination"
457
+ ];
458
+ function isStepType(value) {
459
+ return STEP_TYPES.some((t) => t === value);
460
+ }
420
461
  var TITLE3 = "Simulate Flow";
421
462
  var COMMANDS = [
422
463
  "config",
@@ -426,29 +467,29 @@ var COMMANDS = [
426
467
  ];
427
468
  var inputSchema3 = {
428
469
  configPath: schemas3.SimulateInputShape.configPath,
429
- event: z2.union([z2.record(z2.string(), z2.unknown()), z2.string()]).optional().describe(
470
+ event: z3.union([z3.record(z3.string(), z3.unknown()), z3.string()]).optional().describe(
430
471
  "For destinations: { name, data, consent? }; the event's consent counts toward the destination's consent requirement, on top of state.consent. With command: the command's data, e.g. { marketing: true } for consent. For sources: { content, trigger? } where content is the walkerOS event { name, data }. A web source's page URL goes in trigger.options.url (default http://localhost). Can also be a JSON string or file path."
431
472
  ),
432
473
  flow: schemas3.SimulateInputShape.flow,
433
474
  platform: schemas3.SimulateInputShape.platform,
434
475
  // Override the (optional) CLI `step` shape: the simulate handler hard-requires
435
476
  // a target step (no all-steps mode), so the registered schema must be honest.
436
- step: z2.string().describe(
477
+ step: z3.string().describe(
437
478
  'Required. Target step as "type.name", e.g. "source.demo", "collector.default", "destination.gtag", "transformer.router". A collector step runs enrichment and then collector.next, returning every event the destinations would receive (none after a stop).'
438
479
  ),
439
- verbose: z2.boolean().optional().describe("Include full payload per destination (default: false)"),
440
- ingest: z2.record(z2.string(), z2.unknown()).optional().describe(
480
+ verbose: z3.boolean().optional().describe("Include full payload per destination (default: false)"),
481
+ ingest: z3.record(z3.string(), z3.unknown()).optional().describe(
441
482
  "Pipeline context a step reads via ctx.ingest, e.g. { url } for a request decoder or { ip, userAgent } for a conversion API. Used for transformer, collector and destination steps (their before chains included); _meta is always set by the runtime."
442
483
  ),
443
- state: z2.object({
444
- consent: z2.record(z2.string(), z2.boolean()).optional(),
445
- user: z2.record(z2.string(), z2.unknown()).optional(),
446
- globals: z2.record(z2.string(), z2.unknown()).optional(),
447
- timing: z2.number().optional()
484
+ state: z3.object({
485
+ consent: z3.record(z3.string(), z3.boolean()).optional(),
486
+ user: z3.record(z3.string(), z3.unknown()).optional(),
487
+ globals: z3.record(z3.string(), z3.unknown()).optional(),
488
+ timing: z3.number().optional()
448
489
  }).optional().describe(
449
490
  "Collector state the step starts from. consent: every step (starts a destination that requires consent and feeds its consent check; for a source, the consent a consent-gated source waits for). user/globals/timing: collector steps, seeded before enrichment runs."
450
491
  ),
451
- command: z2.enum(COMMANDS).optional().describe(
492
+ command: z3.enum(COMMANDS).optional().describe(
452
493
  "Destination steps only: run this collector command with event as its data instead of pushing it, as a step example with command does."
453
494
  )
454
495
  };
@@ -472,7 +513,18 @@ function createFlowSimulateToolSpec(client, runtime) {
472
513
  handler: (input) => flowSimulateHandlerBody(client, runtime, input)
473
514
  };
474
515
  }
516
+ function toProperties(value) {
517
+ if (!value) return void 0;
518
+ const properties = {};
519
+ for (const [key, entry] of Object.entries(value)) {
520
+ const property = filterValues(entry);
521
+ if (property !== void 0) properties[key] = property;
522
+ }
523
+ return properties;
524
+ }
475
525
  async function flowSimulateHandlerBody(client, runtime, input) {
526
+ const parsed = parseToolInput(inputSchema3, input);
527
+ if (!parsed.ok) return parsed.error;
476
528
  const {
477
529
  configPath,
478
530
  event,
@@ -483,10 +535,10 @@ async function flowSimulateHandlerBody(client, runtime, input) {
483
535
  ingest,
484
536
  state,
485
537
  command
486
- } = input ?? {};
538
+ } = parsed.data;
487
539
  if (!runtime.simulate) {
488
540
  const refusal = unavailableOperation("simulate");
489
- return mcpError3(refusal, refusal.hint);
541
+ return mcpError5(refusal, refusal.hint);
490
542
  }
491
543
  let known = [];
492
544
  try {
@@ -527,10 +579,25 @@ async function flowSimulateHandlerBody(client, runtime, input) {
527
579
  throw new Error("command applies to destination steps only.");
528
580
  }
529
581
  const resolvedConfigPath = await resolveConfigPath(client, configPath);
530
- known = await knownSecretsOf(runtime, resolvedConfigPath);
582
+ const run = await readRun(runtime, resolvedConfigPath);
583
+ known = run.knownSecrets;
531
584
  const result = await runtime.simulate(
532
585
  resolvedConfigPath,
533
- { stepType, stepId, event: resolvedEvent, flow, ingest, state, command }
586
+ {
587
+ stepType,
588
+ stepId,
589
+ event: resolvedEvent,
590
+ flow,
591
+ ingest,
592
+ state: state && {
593
+ consent: state.consent,
594
+ user: toProperties(state.user),
595
+ globals: toProperties(state.globals),
596
+ timing: state.timing
597
+ },
598
+ command,
599
+ config: run.config
600
+ }
534
601
  );
535
602
  const success = !result.error;
536
603
  const errorMessage = result.error?.message;
@@ -541,7 +608,7 @@ async function flowSimulateHandlerBody(client, runtime, input) {
541
608
  ).length;
542
609
  const summary = `Source captured ${eventCount} event${eventCount !== 1 ? "s" : ""} and ${commandCount} command${commandCount !== 1 ? "s" : ""}`;
543
610
  const shownCalls = verbose ? result.calls : result.calls.filter((call) => call.fn === "elb");
544
- return mcpResult3(
611
+ return mcpResult4(
545
612
  scrubbed(
546
613
  {
547
614
  success,
@@ -563,7 +630,7 @@ async function flowSimulateHandlerBody(client, runtime, input) {
563
630
  );
564
631
  }
565
632
  if (result.step === "transformer") {
566
- return mcpResult3(
633
+ return mcpResult4(
567
634
  scrubbed(
568
635
  {
569
636
  success,
@@ -580,7 +647,7 @@ async function flowSimulateHandlerBody(client, runtime, input) {
580
647
  );
581
648
  }
582
649
  if (result.step === "collector") {
583
- return mcpResult3(
650
+ return mcpResult4(
584
651
  scrubbed(
585
652
  {
586
653
  success,
@@ -620,21 +687,13 @@ async function flowSimulateHandlerBody(client, runtime, input) {
620
687
  ...result.skipped ? { skipped: result.skipped } : {},
621
688
  duration: result.duration
622
689
  };
623
- return mcpResult3(scrubbed(resultObj, known), {
690
+ return mcpResult4(scrubbed(resultObj, known), {
624
691
  next: ["Use flow_bundle to build for production"],
625
692
  ...warnings.length > 0 ? { warnings } : {}
626
693
  });
627
694
  } catch (error) {
628
695
  const hint = "Run flow_validate for detailed error messages";
629
- return scrubbedError(mcpError3(error, refusalHint(error, hint)), known);
630
- }
631
- }
632
- async function knownSecretsOf(runtime, input) {
633
- if (!runtime.knownSecrets) return [];
634
- try {
635
- return await runtime.knownSecrets(input);
636
- } catch {
637
- return [];
696
+ return scrubbedError(mcpError5(error, refusalHint(error, hint)), known);
638
697
  }
639
698
  }
640
699
  function keysOf(consent) {
@@ -666,21 +725,19 @@ function registerFlowSimulateTool(server, client, runtime) {
666
725
  outputSchema: SimulateOutputShape,
667
726
  annotations: spec.annotations
668
727
  },
669
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
670
- // type-erased (input: unknown) => Promise<unknown> form by design.
671
- spec.handler
728
+ (args) => flowSimulateHandlerBody(client, runtime, args)
672
729
  );
673
730
  }
674
731
 
675
732
  // src/tools/push.ts
676
- import { z as z3 } from "zod";
733
+ import { z as z4 } from "zod";
677
734
  import { schemas as schemas4 } from "@walkeros/cli/dev";
678
- import { mcpResult as mcpResult4, mcpError as mcpError4 } from "@walkeros/core";
735
+ import { mcpError as mcpError6 } from "@walkeros/core";
679
736
  var TITLE4 = "Push Events";
680
737
  var DESCRIPTION3 = "Push a real event through a walkerOS flow to actual destinations. Makes real API calls to real endpoints. Best suited for server-side flows \u2014 web flows should use flow_simulate for testing.";
681
738
  var inputSchema4 = {
682
739
  configPath: schemas4.PushInputShape.configPath,
683
- event: z3.record(z3.string(), z3.unknown()).describe(
740
+ event: z4.record(z4.string(), z4.unknown()).describe(
684
741
  'Event object, e.g. { name: "page view", data: { title: "Home" } }'
685
742
  ),
686
743
  flow: schemas4.PushInputShape.flow,
@@ -703,30 +760,42 @@ function createFlowPushToolSpec(runtime) {
703
760
  };
704
761
  }
705
762
  async function flowPushHandlerBody(runtime, input) {
706
- const { configPath, event, flow, platform } = input ?? {};
763
+ const parsed = parseToolInput(inputSchema4, input);
764
+ if (!parsed.ok) return parsed.error;
765
+ const { configPath, event, flow, platform } = parsed.data;
707
766
  if (!runtime.push) {
708
767
  const refusal = unavailableOperation("push");
709
- return mcpError4(refusal, refusal.hint);
768
+ return mcpError6(refusal, refusal.hint);
710
769
  }
770
+ let known = [];
711
771
  try {
772
+ const run = await readRun(runtime, configPath);
773
+ known = run.knownSecrets;
712
774
  const result = await runtime.push(configPath, event, {
713
775
  flow,
714
- platform
776
+ platform,
777
+ config: run.config
715
778
  });
716
779
  if (!result.success) {
717
- return mcpError4(
718
- new Error(result.error || "Push failed"),
719
- "Check destination configuration and connectivity."
780
+ return scrubbedError(
781
+ mcpError6(
782
+ new Error(result.error || "Push failed"),
783
+ "Check destination configuration and connectivity."
784
+ ),
785
+ known
720
786
  );
721
787
  }
722
- return mcpResult4(result);
788
+ return scrubbedPushResult(result, known);
723
789
  } catch (error) {
724
- return mcpError4(
725
- error,
726
- refusalHint(
790
+ return scrubbedError(
791
+ mcpError6(
727
792
  error,
728
- "Check configPath and event format. For web flows, use flow_simulate."
729
- )
793
+ refusalHint(
794
+ error,
795
+ "Check configPath and event format. For web flows, use flow_simulate."
796
+ )
797
+ ),
798
+ known
730
799
  );
731
800
  }
732
801
  }
@@ -742,15 +811,13 @@ function registerFlowPushTool(server, runtime) {
742
811
  outputSchema: PushOutputShape,
743
812
  annotations: spec.annotations
744
813
  },
745
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
746
- // type-erased (input: unknown) => Promise<unknown> form by design.
747
- spec.handler
814
+ (args) => flowPushHandlerBody(runtime, args)
748
815
  );
749
816
  }
750
817
 
751
818
  // src/tools/examples.ts
752
- import { z as z4 } from "zod";
753
- import { fetchPackage, mcpResult as mcpResult5, mcpError as mcpError5 } from "@walkeros/core";
819
+ import { z as z5 } from "zod";
820
+ import { fetchPackage, mcpResult as mcpResult5, mcpError as mcpError7 } from "@walkeros/core";
754
821
  import { resolveExportName, selectDevExamples } from "@walkeros/cli";
755
822
 
756
823
  // src/catalog.ts
@@ -758,7 +825,7 @@ var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
758
825
  var JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm";
759
826
  var WALKEROS_JSON_PATH = "dist/walkerOS.json";
760
827
  var CACHE_TTL = 5 * 60 * 1e3;
761
- var CLIENT_HEADER = "walkeros-mcp/4.7.0-next-1790722899482";
828
+ var CLIENT_HEADER = "walkeros-mcp/4.7.0";
762
829
  function getPackageBaseUrl() {
763
830
  return process.env.WALKEROS_APP_URL || void 0;
764
831
  }
@@ -910,20 +977,53 @@ function applyFilters(entries, filters) {
910
977
  return results;
911
978
  }
912
979
 
980
+ // src/tools/narrow.ts
981
+ import { schemas as schemas5 } from "@walkeros/core/dev";
982
+ function isRecord(value) {
983
+ return typeof value === "object" && value !== null && !Array.isArray(value);
984
+ }
985
+ function stringField(value, key) {
986
+ if (!isRecord(value)) return void 0;
987
+ const field = value[key];
988
+ return typeof field === "string" ? field : void 0;
989
+ }
990
+ function recordField(value, key) {
991
+ if (!isRecord(value)) return void 0;
992
+ const field = value[key];
993
+ return isRecord(field) ? field : void 0;
994
+ }
995
+ function fieldsOf(value) {
996
+ return isRecord(value) ? value : {};
997
+ }
998
+ function isFlowJson(value) {
999
+ return isRecord(value) && isRecord(value.flows);
1000
+ }
1001
+ function stepExamplesOf(value) {
1002
+ if (!isRecord(value)) return void 0;
1003
+ const examples = {};
1004
+ const skipped = [];
1005
+ for (const [name, example] of Object.entries(value)) {
1006
+ const parsed = schemas5.FlowSchemas.StepExampleSchema.safeParse(example);
1007
+ if (parsed.success) examples[name] = parsed.data;
1008
+ else skipped.push(name);
1009
+ }
1010
+ return { examples, skipped };
1011
+ }
1012
+
913
1013
  // src/tools/examples.ts
914
1014
  var MAX_PACKAGE_LOOKUPS = 25;
915
1015
  var TITLE5 = "Flow Examples";
916
1016
  var DESCRIPTION4 = 'List all step examples in a walkerOS flow configuration. Shows example names, step locations, and in/out shapes. Inline examples on a step take precedence; steps without inline examples fall back to the examples shipped by their referenced package. Each result is tagged with its source ("inline" or "package"). On the hosted server configPath accepts only inline JSON or a saved flow id (flow_ or cfg_), no file paths or URLs. Use this to discover available test fixtures and simulation data.';
917
1017
  var inputSchema5 = {
918
- configPath: z4.string().min(1).describe(
1018
+ configPath: z5.string().min(1).describe(
919
1019
  "Inline JSON string, file path, or URL of a flow configuration (hosted server: inline JSON or a saved flow id only)"
920
1020
  ),
921
- flow: z4.string().optional().describe("Flow name for multi-flow configs"),
922
- step: z4.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
923
- full: z4.boolean().optional().describe(
1021
+ flow: z5.string().optional().describe("Flow name for multi-flow configs"),
1022
+ step: z5.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
1023
+ full: z5.boolean().optional().describe(
924
1024
  "Return full in/out/mapping data for each example (default: false, returns metadata only)"
925
1025
  ),
926
- includeHidden: z4.boolean().optional().describe(
1026
+ includeHidden: z5.boolean().optional().describe(
927
1027
  "Include examples marked public: false (default: false). Set true for test/debug discovery."
928
1028
  )
929
1029
  };
@@ -944,9 +1044,14 @@ function createFlowExamplesToolSpec(runtime) {
944
1044
  };
945
1045
  }
946
1046
  async function flowExamplesHandlerBody(runtime, input) {
947
- const { configPath, flow, step, full, includeHidden } = input ?? {};
1047
+ const parsed = parseToolInput(inputSchema5, input);
1048
+ if (!parsed.ok) return parsed.error;
1049
+ const { configPath, flow, step, full, includeHidden } = parsed.data;
948
1050
  try {
949
1051
  const rawConfig = await runtime.load(configPath);
1052
+ if (!isFlowJson(rawConfig)) {
1053
+ throw new Error("Config is not a flow config: it has no flows object.");
1054
+ }
950
1055
  const flowNames = Object.keys(rawConfig.flows || {});
951
1056
  const flowName = flow || (flowNames.length === 1 ? flowNames[0] : void 0);
952
1057
  if (!flowName) {
@@ -999,6 +1104,7 @@ async function flowExamplesHandlerBody(runtime, input) {
999
1104
  };
1000
1105
  const packageLookups = /* @__PURE__ */ new Map();
1001
1106
  let skippedPackages = false;
1107
+ const skippedExamples = [];
1002
1108
  const packageFor = (packageName) => {
1003
1109
  const existing = packageLookups.get(packageName);
1004
1110
  if (existing) return existing;
@@ -1015,10 +1121,7 @@ async function flowExamplesHandlerBody(runtime, input) {
1015
1121
  { examples: info.examples, exportExamples: info.exportExamples },
1016
1122
  exportName
1017
1123
  );
1018
- const stepExamples = selected?.step;
1019
- if (stepExamples && typeof stepExamples === "object")
1020
- return stepExamples;
1021
- return void 0;
1124
+ return stepExamplesOf(selected?.step);
1022
1125
  };
1023
1126
  const stepTypes = [
1024
1127
  { key: "sources", type: "source" },
@@ -1030,9 +1133,7 @@ async function flowExamplesHandlerBody(runtime, input) {
1030
1133
  for (const [name, ref] of Object.entries(refs)) {
1031
1134
  if (step && `${type}.${name}` !== step) continue;
1032
1135
  if (ref.examples) {
1033
- examples.push(
1034
- ...toItems(ref.examples, type, name, "inline")
1035
- );
1136
+ examples.push(...toItems(ref.examples, type, name, "inline"));
1036
1137
  continue;
1037
1138
  }
1038
1139
  if (!ref.package) continue;
@@ -1040,8 +1141,12 @@ async function flowExamplesHandlerBody(runtime, input) {
1040
1141
  if (!info) continue;
1041
1142
  const { exportName } = resolveExportName(flowSettings, type, name);
1042
1143
  const packageExamples = stepExamplesFor(info, exportName);
1043
- if (packageExamples)
1044
- examples.push(...toItems(packageExamples, type, name, "package"));
1144
+ if (!packageExamples) continue;
1145
+ examples.push(
1146
+ ...toItems(packageExamples.examples, type, name, "package")
1147
+ );
1148
+ for (const exampleName of packageExamples.skipped)
1149
+ skippedExamples.push(`${type}.${name}.${exampleName}`);
1045
1150
  }
1046
1151
  }
1047
1152
  const result = {
@@ -1055,6 +1160,11 @@ async function flowExamplesHandlerBody(runtime, input) {
1055
1160
  "No examples found. Add examples to step entries, or reference a package that ships examples (see package_get)."
1056
1161
  );
1057
1162
  }
1163
+ if (skippedExamples.length > 0) {
1164
+ warnings.push(
1165
+ `Skipped ${skippedExamples.length} package example(s) that do not match the step example schema: ${skippedExamples.join(", ")}.`
1166
+ );
1167
+ }
1058
1168
  if (skippedPackages) {
1059
1169
  warnings.push(
1060
1170
  `Package examples were looked up for the first ${MAX_PACKAGE_LOOKUPS} packages only. Use step to narrow the result.`
@@ -1068,7 +1178,7 @@ async function flowExamplesHandlerBody(runtime, input) {
1068
1178
  };
1069
1179
  return mcpResult5(result, hints);
1070
1180
  } catch (error) {
1071
- return mcpError5(
1181
+ return mcpError7(
1072
1182
  error,
1073
1183
  refusalHint(error, "Check configPath \u2014 expected a flow.json file")
1074
1184
  );
@@ -1086,25 +1196,23 @@ function registerFlowExamplesTool(server, runtime) {
1086
1196
  outputSchema: ExamplesListOutputShape,
1087
1197
  annotations: spec.annotations
1088
1198
  },
1089
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1090
- // type-erased (input: unknown) => Promise<unknown> form by design.
1091
- spec.handler
1199
+ (args) => flowExamplesHandlerBody(runtime, args)
1092
1200
  );
1093
1201
  }
1094
1202
 
1095
1203
  // src/tools/package.ts
1096
- import { z as z5 } from "zod";
1097
- import { fetchPackage as fetchPackage2, mcpResult as mcpResult6, mcpError as mcpError6 } from "@walkeros/core";
1204
+ import { z as z6 } from "zod";
1205
+ import { fetchPackage as fetchPackage2, mcpResult as mcpResult6, mcpError as mcpError8 } from "@walkeros/core";
1098
1206
  import { mergeConfigSchema } from "@walkeros/core/dev";
1099
1207
  var SEARCH_TITLE = "Search Package";
1100
1208
  var SEARCH_DESCRIPTION = "Start here for package discovery. Never guess package names: use this tool first to find exact names. Without package name: returns catalog filtered by type/platform. With package name: returns metadata, hint keys, and example summaries.";
1101
1209
  var searchInputSchema = {
1102
- package: z5.string().min(1).optional().describe(
1210
+ package: z6.string().min(1).optional().describe(
1103
1211
  "Exact npm package name for detailed lookup (e.g., @walkeros/web-destination-snowplow)"
1104
1212
  ),
1105
- type: z5.enum(["source", "destination", "transformer", "store"]).optional().describe("Filter by package type (browse mode)"),
1106
- platform: z5.enum(["web", "server"]).optional().describe("Filter by platform (browse mode, includes universal packages)"),
1107
- version: z5.string().optional().describe("Package version for detailed lookup (default: latest)")
1213
+ type: z6.enum(["source", "destination", "transformer", "store"]).optional().describe("Filter by package type (browse mode)"),
1214
+ platform: z6.enum(["web", "server"]).optional().describe("Filter by platform (browse mode, includes universal packages)"),
1215
+ version: z6.string().optional().describe("Package version for detailed lookup (default: latest)")
1108
1216
  };
1109
1217
  var searchAnnotations = {
1110
1218
  readOnlyHint: true,
@@ -1123,12 +1231,9 @@ function createPackageSearchToolSpec() {
1123
1231
  };
1124
1232
  }
1125
1233
  async function packageSearchHandlerBody(input) {
1126
- const {
1127
- package: packageName,
1128
- type,
1129
- platform,
1130
- version
1131
- } = input ?? {};
1234
+ const parsed = parseToolInput(searchInputSchema, input);
1235
+ if (!parsed.ok) return parsed.error;
1236
+ const { package: packageName, type, platform, version } = parsed.data;
1132
1237
  const baseUrl = getPackageBaseUrl();
1133
1238
  if (!packageName) {
1134
1239
  const { entries, warnings } = await fetchCatalog({
@@ -1161,7 +1266,7 @@ async function packageSearchHandlerBody(input) {
1161
1266
  next: ["Use package_get for schemas and examples"]
1162
1267
  });
1163
1268
  } catch (error) {
1164
- return mcpError6(
1269
+ return mcpError8(
1165
1270
  error,
1166
1271
  "Package not found. Use package_search without parameters to browse available packages."
1167
1272
  );
@@ -1178,19 +1283,17 @@ function registerPackageSearchTool(server) {
1178
1283
  // No outputSchema: browse mode returns {catalog, count}, lookup returns metadata — incompatible shapes
1179
1284
  annotations: spec.annotations
1180
1285
  },
1181
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1182
- // type-erased (input: unknown) => Promise<unknown> form by design.
1183
- spec.handler
1286
+ (args) => packageSearchHandlerBody(args)
1184
1287
  );
1185
1288
  }
1186
1289
  var GET_TITLE = "Get Package";
1187
1290
  var GET_DESCRIPTION = 'Requires exact package name: do not guess names, use package_search first to find them. Returns schemas + hint texts + example summaries by default (lightweight). Use section parameter for full content: "hints" (with code blocks), "examples" (full in/out data), or "all".';
1188
1291
  var getInputSchema = {
1189
- package: z5.string().min(1).describe(
1292
+ package: z6.string().min(1).describe(
1190
1293
  "Exact npm package name (e.g., @walkeros/web-destination-snowplow)"
1191
1294
  ),
1192
- version: z5.string().optional().describe("Package version (default: latest)"),
1193
- section: z5.enum(["hints", "examples", "all"]).optional().describe(
1295
+ version: z6.string().optional().describe("Package version (default: latest)"),
1296
+ section: z6.enum(["hints", "examples", "all"]).optional().describe(
1194
1297
  "Section to expand with full content. Default: summary view with schemas + hint texts + example descriptions"
1195
1298
  )
1196
1299
  };
@@ -1210,12 +1313,35 @@ function createPackageGetToolSpec() {
1210
1313
  handler: (input) => packageGetHandlerBody(input)
1211
1314
  };
1212
1315
  }
1316
+ var STEP_PACKAGE_TYPES = [
1317
+ "source",
1318
+ "destination",
1319
+ "transformer",
1320
+ "store"
1321
+ ];
1322
+ function isStepPackageType(type) {
1323
+ return STEP_PACKAGE_TYPES.some((stepType) => stepType === type);
1324
+ }
1325
+ function shapeSchemas(type, schemas8) {
1326
+ const shaped = {};
1327
+ if (isStepPackageType(type)) {
1328
+ const settings = recordField(schemas8, "settings");
1329
+ const credentials = recordField(schemas8, "credentials");
1330
+ shaped.config = mergeConfigSchema(type, {
1331
+ ...schemas8,
1332
+ settings,
1333
+ credentials
1334
+ });
1335
+ }
1336
+ for (const [key, value] of Object.entries(schemas8)) {
1337
+ if (key !== "settings") shaped[key] = value;
1338
+ }
1339
+ return shaped;
1340
+ }
1213
1341
  async function packageGetHandlerBody(input) {
1214
- const {
1215
- package: packageName,
1216
- version,
1217
- section
1218
- } = input ?? {};
1342
+ const parsed = parseToolInput(getInputSchema, input);
1343
+ if (!parsed.ok) return parsed.error;
1344
+ const { package: packageName, version, section } = parsed.data;
1219
1345
  const baseUrl = getPackageBaseUrl();
1220
1346
  try {
1221
1347
  const info = await fetchPackage2(packageName, {
@@ -1223,33 +1349,29 @@ async function packageGetHandlerBody(input) {
1223
1349
  baseUrl,
1224
1350
  client: CLIENT_HEADER
1225
1351
  });
1226
- const mergedSchemas = {};
1227
- if (info.type) {
1228
- mergedSchemas.config = mergeConfigSchema(
1229
- info.type,
1230
- info.schemas
1231
- );
1232
- }
1233
- for (const [key, value] of Object.entries(info.schemas)) {
1234
- if (key !== "settings") {
1235
- mergedSchemas[key] = value;
1236
- }
1237
- }
1238
1352
  const result = {
1239
1353
  package: info.packageName,
1240
1354
  version: info.version,
1241
1355
  type: info.type,
1242
1356
  platform: normalizePlatform(info.platform),
1243
- schemas: mergedSchemas
1357
+ schemas: shapeSchemas(info.type, info.schemas)
1244
1358
  };
1359
+ if (info.exportSchemas) {
1360
+ result.exportSchemas = Object.fromEntries(
1361
+ Object.entries(info.exportSchemas).map(([name, schemas8]) => [
1362
+ name,
1363
+ shapeSchemas(info.type, schemas8)
1364
+ ])
1365
+ );
1366
+ }
1245
1367
  if (info.hints) {
1246
1368
  if (section === "hints" || section === "all") {
1247
1369
  result.hints = info.hints;
1248
1370
  } else {
1249
1371
  const hintSummary = {};
1250
1372
  for (const [key, hint] of Object.entries(info.hints)) {
1251
- const h = hint;
1252
- hintSummary[key] = { text: h.text };
1373
+ const text = stringField(hint, "text");
1374
+ if (text !== void 0) hintSummary[key] = { text };
1253
1375
  }
1254
1376
  result.hints = hintSummary;
1255
1377
  }
@@ -1262,7 +1384,7 @@ async function packageGetHandlerBody(input) {
1262
1384
  }
1263
1385
  return mcpResult6(result);
1264
1386
  } catch (error) {
1265
- return mcpError6(
1387
+ return mcpError8(
1266
1388
  error,
1267
1389
  "Use package_search to browse available package names."
1268
1390
  );
@@ -1279,15 +1401,13 @@ function registerGetPackageSchemaTool(server) {
1279
1401
  // No outputSchema: removed to avoid SDK -32602 crashes on unexpected field values
1280
1402
  annotations: spec.annotations
1281
1403
  },
1282
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1283
- // type-erased (input: unknown) => Promise<unknown> form by design.
1284
- spec.handler
1404
+ (args) => packageGetHandlerBody(args)
1285
1405
  );
1286
1406
  }
1287
1407
 
1288
1408
  // src/tools/flow-load.ts
1289
- import { z as z6 } from "zod";
1290
- import { mcpResult as mcpResult7, mcpError as mcpError7 } from "@walkeros/core";
1409
+ import { z as z7 } from "zod";
1410
+ import { mcpResult as mcpResult7, mcpError as mcpError9 } from "@walkeros/core";
1291
1411
 
1292
1412
  // src/types.ts
1293
1413
  function isAuthError(error) {
@@ -1394,16 +1514,16 @@ var SERVER_SKELETON = {
1394
1514
  var TITLE6 = "Load or Create Flow";
1395
1515
  var DESCRIPTION5 = "Load an existing flow configuration from a local file path, URL, or walkerOS API (by flow ID). On the hosted server only inline JSON or a saved flow id is accepted, no file paths or URLs. Or create a new empty flow by specifying a platform (web or server). Use the add-step prompt to add sources, destinations, transformers, or stores to the flow.";
1396
1516
  var inputSchema6 = {
1397
- source: z6.string().optional().describe(
1517
+ source: z7.string().optional().describe(
1398
1518
  "Flow source: local file path (./flow.json), URL (https://...), inline JSON string, or API flow ID (cfg_...). Omit to create a new flow."
1399
1519
  ),
1400
- platform: z6.enum(["web", "server"]).optional().describe(
1520
+ platform: z7.enum(["web", "server"]).optional().describe(
1401
1521
  "Platform for new flows. Required when source is omitted. web = browser tracking, server = Node.js HTTP."
1402
1522
  )
1403
1523
  };
1404
1524
  var outputSchema = {
1405
- version: z6.number().describe("Flow config version"),
1406
- flows: z6.record(z6.string(), z6.unknown()).describe("Flow entries")
1525
+ version: z7.number().describe("Flow config version"),
1526
+ flows: z7.record(z7.string(), z7.unknown()).describe("Flow entries")
1407
1527
  };
1408
1528
  var annotations6 = {
1409
1529
  readOnlyHint: true,
@@ -1422,11 +1542,13 @@ function createFlowLoadToolSpec(client, runtime) {
1422
1542
  };
1423
1543
  }
1424
1544
  async function flowLoadHandlerBody(client, runtime, input) {
1425
- const { source, platform } = input ?? {};
1545
+ const parsed = parseToolInput(inputSchema6, input);
1546
+ if (!parsed.ok) return parsed.error;
1547
+ const { source, platform } = parsed.data;
1426
1548
  if (source && isCloudId(source)) {
1427
1549
  const resolvedProjectId = resolveDefaultProject(client, void 0);
1428
1550
  if (!resolvedProjectId) {
1429
- return mcpError7(new Error(NO_DEFAULT_PROJECT_ERROR));
1551
+ return mcpError9(new Error(NO_DEFAULT_PROJECT_ERROR));
1430
1552
  }
1431
1553
  try {
1432
1554
  const flow = await client.getFlow({
@@ -1440,7 +1562,7 @@ async function flowLoadHandlerBody(client, runtime, input) {
1440
1562
  }
1441
1563
  );
1442
1564
  } catch (error) {
1443
- return mcpError7(error, isAuthError(error) ? AUTH_HINT : void 0);
1565
+ return mcpError9(error, isAuthError(error) ? AUTH_HINT : void 0);
1444
1566
  }
1445
1567
  }
1446
1568
  try {
@@ -1451,7 +1573,7 @@ async function flowLoadHandlerBody(client, runtime, input) {
1451
1573
  });
1452
1574
  }
1453
1575
  if (!platform) {
1454
- return mcpError7(
1576
+ return mcpError9(
1455
1577
  new Error(
1456
1578
  "Provide source (file path, URL, or flow ID) to load existing flow, or platform (web/server) to create a new one."
1457
1579
  )
@@ -1465,11 +1587,11 @@ async function flowLoadHandlerBody(client, runtime, input) {
1465
1587
  ]
1466
1588
  });
1467
1589
  } catch (error) {
1468
- if (error instanceof RuntimeRefusal) return mcpError7(error, error.hint);
1590
+ if (error instanceof RuntimeRefusal) return mcpError9(error, error.hint);
1469
1591
  const msg = error instanceof Error ? error.message : "";
1470
1592
  if (msg.includes("not found") || msg.includes("ENOENT"))
1471
- return mcpError7(error, "Check configPath \u2014 expected a flow.json file");
1472
- return mcpError7(error);
1593
+ return mcpError9(error, "Check configPath \u2014 expected a flow.json file");
1594
+ return mcpError9(error);
1473
1595
  }
1474
1596
  }
1475
1597
  function registerFlowLoadTool(server, client, runtime) {
@@ -1484,20 +1606,18 @@ function registerFlowLoadTool(server, client, runtime) {
1484
1606
  outputSchema,
1485
1607
  annotations: spec.annotations
1486
1608
  },
1487
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1488
- // type-erased (input: unknown) => Promise<unknown> form by design.
1489
- spec.handler
1609
+ (args) => flowLoadHandlerBody(client, runtime, args)
1490
1610
  );
1491
1611
  }
1492
1612
 
1493
1613
  // src/tools/feedback.ts
1494
- import { z as z7 } from "zod";
1495
- import { mcpResult as mcpResult8, mcpError as mcpError8 } from "@walkeros/core";
1614
+ import { z as z8 } from "zod";
1615
+ import { mcpResult as mcpResult8, mcpError as mcpError10 } from "@walkeros/core";
1496
1616
  var TITLE7 = "Send Feedback";
1497
1617
  var DESCRIPTION6 = "Send feedback about walkerOS";
1498
1618
  var inputSchema7 = {
1499
- text: z7.string().describe("Your feedback text"),
1500
- anonymous: z7.boolean().optional().describe(
1619
+ text: z8.string().describe("Your feedback text"),
1620
+ anonymous: z8.boolean().optional().describe(
1501
1621
  "Include user/project info? false = include, true = anonymous. Only needed on first call if not yet configured."
1502
1622
  )
1503
1623
  };
@@ -1518,7 +1638,9 @@ function createFeedbackToolSpec(client) {
1518
1638
  };
1519
1639
  }
1520
1640
  async function feedbackHandlerBody(client, input) {
1521
- const { text, anonymous: explicitAnonymous } = input ?? {};
1641
+ const parsed = parseToolInput(inputSchema7, input);
1642
+ if (!parsed.ok) return parsed.error;
1643
+ const { text, anonymous: explicitAnonymous } = parsed.data;
1522
1644
  try {
1523
1645
  let anonymous = client.getFeedbackPreference();
1524
1646
  if (anonymous === void 0 && explicitAnonymous === void 0) {
@@ -1539,11 +1661,11 @@ async function feedbackHandlerBody(client, input) {
1539
1661
  const isAnonymous = explicitAnonymous ?? anonymous ?? true;
1540
1662
  await client.submitFeedback(text, {
1541
1663
  anonymous: isAnonymous,
1542
- version: "4.7.0-next-1790722899482"
1664
+ version: "4.7.0"
1543
1665
  });
1544
1666
  return mcpResult8({ ok: true });
1545
1667
  } catch (error) {
1546
- return mcpError8(error);
1668
+ return mcpError10(error);
1547
1669
  }
1548
1670
  }
1549
1671
  function registerFeedbackTool(server, client) {
@@ -1556,9 +1678,7 @@ function registerFeedbackTool(server, client) {
1556
1678
  inputSchema: spec.inputSchema,
1557
1679
  annotations: spec.annotations
1558
1680
  },
1559
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1560
- // type-erased (input: unknown) => Promise<unknown> form by design.
1561
- spec.handler
1681
+ (args) => feedbackHandlerBody(client, args)
1562
1682
  );
1563
1683
  }
1564
1684
 
@@ -21271,20 +21391,18 @@ function registerDiagnosticsTool(server, client, packageVersion) {
21271
21391
  inputSchema: spec.inputSchema,
21272
21392
  annotations: spec.annotations
21273
21393
  },
21274
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
21275
- // type-erased (input: unknown) => Promise<unknown> form by design.
21276
- spec.handler
21394
+ () => diagnosticsHandlerBody(client, packageVersion)
21277
21395
  );
21278
21396
  }
21279
21397
 
21280
21398
  // src/tools/auth.ts
21281
- import { z as z8 } from "zod";
21282
- import { mcpResult as mcpResult10, mcpError as mcpError9 } from "@walkeros/core";
21399
+ import { z as z9 } from "zod";
21400
+ import { mcpResult as mcpResult10, mcpError as mcpError11 } from "@walkeros/core";
21283
21401
  var TITLE9 = "Authentication";
21284
21402
  var DESCRIPTION8 = "Manage walkerOS authentication. Check login status, log in via the device authorization grant, or log out. No terminal or browser required, the MCP client handles the authorization URL.";
21285
21403
  var inputSchema9 = {
21286
- action: z8.enum(["status", "login", "logout"]).describe("Authentication action to perform"),
21287
- deviceCode: z8.string().optional().describe(
21404
+ action: z9.enum(["status", "login", "logout"]).describe("Authentication action to perform"),
21405
+ deviceCode: z9.string().optional().describe(
21288
21406
  "Device code from a previous pending login attempt. Provide to resume polling without requesting a new code."
21289
21407
  )
21290
21408
  };
@@ -21305,7 +21423,9 @@ function createAuthToolSpec(client) {
21305
21423
  };
21306
21424
  }
21307
21425
  async function authHandlerBody(client, input) {
21308
- const { action, deviceCode } = input ?? {};
21426
+ const parsed = parseToolInput(inputSchema9, input);
21427
+ if (!parsed.ok) return parsed.error;
21428
+ const { action, deviceCode } = parsed.data;
21309
21429
  try {
21310
21430
  switch (action) {
21311
21431
  case "status": {
@@ -21318,7 +21438,7 @@ async function authHandlerBody(client, input) {
21318
21438
  const user = await client.whoami();
21319
21439
  return mcpResult10({
21320
21440
  authenticated: true,
21321
- ...user
21441
+ ...fieldsOf(user)
21322
21442
  });
21323
21443
  }
21324
21444
  case "login": {
@@ -21346,14 +21466,14 @@ async function authHandlerBody(client, input) {
21346
21466
  });
21347
21467
  }
21348
21468
  if (poll.status === "denied")
21349
- return mcpError9(new Error("Authorization was denied."));
21469
+ return mcpError11(new Error("Authorization was denied."));
21350
21470
  if (poll.status === "expired")
21351
- return mcpError9(
21471
+ return mcpError11(
21352
21472
  new Error(
21353
21473
  'The one-time code expired. Run auth with action "login" for a new one.'
21354
21474
  )
21355
21475
  );
21356
- return mcpError9(new Error(poll.error));
21476
+ return mcpError11(new Error(poll.error));
21357
21477
  }
21358
21478
  const code = await client.requestDeviceCode();
21359
21479
  const loginUrl = code.verificationUriComplete;
@@ -21390,7 +21510,7 @@ async function authHandlerBody(client, input) {
21390
21510
  );
21391
21511
  }
21392
21512
  } catch (error) {
21393
- return mcpError9(error);
21513
+ return mcpError11(error);
21394
21514
  }
21395
21515
  }
21396
21516
  function registerAuthTool(server, client) {
@@ -21403,15 +21523,13 @@ function registerAuthTool(server, client) {
21403
21523
  inputSchema: spec.inputSchema,
21404
21524
  annotations: spec.annotations
21405
21525
  },
21406
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
21407
- // type-erased (input: unknown) => Promise<unknown> form by design.
21408
- spec.handler
21526
+ (args) => authHandlerBody(client, args)
21409
21527
  );
21410
21528
  }
21411
21529
 
21412
21530
  // src/tools/project-manage.ts
21413
- import { z as z9 } from "zod";
21414
- import { mcpResult as mcpResult11, mcpError as mcpError10 } from "@walkeros/core";
21531
+ import { z as z10 } from "zod";
21532
+ import { mcpResult as mcpResult11, mcpError as mcpError12 } from "@walkeros/core";
21415
21533
 
21416
21534
  // src/action-requirements.ts
21417
21535
  function validateActionInput(toolName, action, input, map) {
@@ -21532,21 +21650,22 @@ var FRAME_MANAGE_REQUIREMENTS = {
21532
21650
  };
21533
21651
 
21534
21652
  // src/tools/project-manage.ts
21535
- function wrapProjectName(p) {
21536
- return p.name !== void 0 ? { ...p, name: wrapUserData(p.name) } : p;
21653
+ function wrapProjectName(project) {
21654
+ const name = stringField(project, "name");
21655
+ return isRecord(project) && name !== void 0 ? { ...project, name: wrapUserData(name) } : project;
21537
21656
  }
21538
21657
  var TITLE10 = "Project Management";
21539
21658
  var DESCRIPTION9 = "Manage walkerOS projects. List, create, update, delete projects, or set a default project for CLI operations.";
21540
21659
  var inputSchema10 = {
21541
- action: z9.enum(["list", "get", "create", "update", "delete", "set_default"]).describe("Project management action to perform"),
21542
- projectId: z9.string().optional().describe("Required for get, update, delete, set_default."),
21543
- name: z9.string().optional().describe(
21660
+ action: z10.enum(["list", "get", "create", "update", "delete", "set_default"]).describe("Project management action to perform"),
21661
+ projectId: z10.string().optional().describe("Required for get, update, delete, set_default."),
21662
+ name: z10.string().optional().describe(
21544
21663
  "Required for create and update (update also requires projectId)."
21545
21664
  ),
21546
- cursor: z9.string().optional().describe(
21665
+ cursor: z10.string().optional().describe(
21547
21666
  "Pagination cursor from a previous list response. Only used with the list action."
21548
21667
  ),
21549
- limit: z9.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action.")
21668
+ limit: z10.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action.")
21550
21669
  };
21551
21670
  var annotations10 = {
21552
21671
  readOnlyHint: false,
@@ -21565,19 +21684,22 @@ function createProjectManageToolSpec(client) {
21565
21684
  };
21566
21685
  }
21567
21686
  async function projectManageHandlerBody(client, input) {
21568
- const { action, projectId, name, cursor, limit } = input ?? {};
21687
+ const parsed = parseToolInput(inputSchema10, input);
21688
+ if (!parsed.ok) return parsed.error;
21689
+ const { action, projectId, name, cursor, limit } = parsed.data;
21569
21690
  const validationError = validateActionInput(
21570
21691
  "project_manage",
21571
21692
  action ?? "",
21572
21693
  { projectId, name },
21573
21694
  PROJECT_MANAGE_REQUIREMENTS
21574
21695
  );
21575
- if (validationError) return mcpError10(new Error(validationError));
21696
+ if (validationError) return mcpError12(new Error(validationError));
21576
21697
  try {
21577
21698
  switch (action) {
21578
21699
  case "list": {
21579
21700
  const projects = await client.listProjects({ cursor, limit });
21580
- const items = Array.isArray(projects) ? projects : projects?.projects || [];
21701
+ const listed = isRecord(projects) ? projects.projects : void 0;
21702
+ const items = Array.isArray(projects) ? projects : Array.isArray(listed) ? listed : [];
21581
21703
  if (items.length === 0) {
21582
21704
  return mcpResult11(
21583
21705
  { projects: [] },
@@ -21589,8 +21711,7 @@ async function projectManageHandlerBody(client, input) {
21589
21711
  }
21590
21712
  );
21591
21713
  }
21592
- const typedItems = items;
21593
- const safe = Array.isArray(projects) ? typedItems.map(wrapProjectName) : { ...projects, projects: typedItems.map(wrapProjectName) };
21714
+ const safe = Array.isArray(projects) ? items.map(wrapProjectName) : { ...fieldsOf(projects), projects: items.map(wrapProjectName) };
21594
21715
  return mcpResult11(safe);
21595
21716
  }
21596
21717
  case "get": {
@@ -21633,7 +21754,7 @@ async function projectManageHandlerBody(client, input) {
21633
21754
  );
21634
21755
  }
21635
21756
  } catch (error) {
21636
- return mcpError10(error, isAuthError(error) ? AUTH_HINT : void 0);
21757
+ return mcpError12(error, isAuthError(error) ? AUTH_HINT : void 0);
21637
21758
  }
21638
21759
  }
21639
21760
  function registerProjectManageTool(server, client) {
@@ -21646,15 +21767,13 @@ function registerProjectManageTool(server, client) {
21646
21767
  inputSchema: spec.inputSchema,
21647
21768
  annotations: spec.annotations
21648
21769
  },
21649
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
21650
- // type-erased (input: unknown) => Promise<unknown> form by design.
21651
- spec.handler
21770
+ (args) => projectManageHandlerBody(client, args)
21652
21771
  );
21653
21772
  }
21654
21773
 
21655
21774
  // src/tools/flow-manage.ts
21656
- import { z as z10 } from "zod";
21657
- import { mcpResult as mcpResult12, mcpError as mcpError11 } from "@walkeros/core";
21775
+ import { z as z11 } from "zod";
21776
+ import { mcpResult as mcpResult12, mcpError as mcpError13 } from "@walkeros/core";
21658
21777
 
21659
21778
  // src/ui-parts.ts
21660
21779
  function flowCanvasResult(payload) {
@@ -21759,19 +21878,10 @@ var links = {
21759
21878
 
21760
21879
  // src/tools/flow-manage.ts
21761
21880
  function pickPlatform(content) {
21762
- if (content && typeof content === "object") {
21763
- const flows = content.flows;
21764
- if (flows && typeof flows === "object") {
21765
- for (const flow of Object.values(flows)) {
21766
- if (flow && typeof flow === "object") {
21767
- const config = flow.config;
21768
- if (config && typeof config === "object") {
21769
- const platform = config.platform;
21770
- if (platform === "web" || platform === "server") return platform;
21771
- }
21772
- }
21773
- }
21774
- }
21881
+ const flows = recordField(content, "flows");
21882
+ for (const flow of Object.values(flows ?? {})) {
21883
+ const platform = stringField(recordField(flow, "config"), "platform");
21884
+ if (platform === "web" || platform === "server") return platform;
21775
21885
  }
21776
21886
  return "server";
21777
21887
  }
@@ -21780,22 +21890,29 @@ function flowPageUrl(client, projectId, flowId) {
21780
21890
  return links.flow({ baseUrl: client.appBaseUrl(), projectId, flowId });
21781
21891
  }
21782
21892
  function safeSummary(flow) {
21783
- return flow.name !== void 0 ? { ...flow, name: wrapUserData(flow.name) } : flow;
21893
+ const name = stringField(flow, "name");
21894
+ return isRecord(flow) && name !== void 0 ? { ...flow, name: wrapUserData(name) } : flow;
21784
21895
  }
21785
21896
  function safeDetail(flow) {
21786
- const withName = flow.name !== void 0 ? { ...flow, name: wrapUserData(flow.name) } : { ...flow };
21787
- if (flow.config !== void 0) {
21788
- withName.config = redactNestedStrings(
21789
- flow.config,
21790
- { skip: keepStructural }
21791
- );
21897
+ const fields = { ...fieldsOf(flow) };
21898
+ const name = stringField(fields, "name");
21899
+ if (name !== void 0) fields.name = wrapUserData(name);
21900
+ if (fields.config !== void 0) {
21901
+ fields.config = redactNestedStrings(fields.config, {
21902
+ skip: keepStructural
21903
+ });
21792
21904
  }
21793
- return withName;
21905
+ return {
21906
+ fields,
21907
+ id: stringField(fields, "id"),
21908
+ name: stringField(fields, "name"),
21909
+ config: recordField(fields, "config")
21910
+ };
21794
21911
  }
21795
21912
  var TITLE11 = "Flow Management";
21796
21913
  var DESCRIPTION10 = "Manage walkerOS flows and their previews. List/get/create/update/delete/duplicate flows, or create/inspect/delete preview bundles and mint activation grants (preview_regrant) for testing flow changes on live sites.";
21797
21914
  var inputSchema11 = {
21798
- action: z10.enum([
21915
+ action: z11.enum([
21799
21916
  "list",
21800
21917
  "get",
21801
21918
  "create",
@@ -21808,52 +21925,52 @@ var inputSchema11 = {
21808
21925
  "preview_delete",
21809
21926
  "preview_regrant"
21810
21927
  ]).describe("Flow management action to perform"),
21811
- flowId: z10.string().optional().describe(
21928
+ flowId: z11.string().optional().describe(
21812
21929
  "Flow ID (flow_...) or config ID (cfg_...). Required for get, update, delete, duplicate, preview_list, preview_get, preview_create, preview_delete, preview_regrant."
21813
21930
  ),
21814
- projectId: z10.string().optional().describe(
21931
+ projectId: z11.string().optional().describe(
21815
21932
  "Project ID. Optional filter for list (omit to list all projects). Required for create if no default project set."
21816
21933
  ),
21817
- name: z10.string().optional().describe(
21934
+ name: z11.string().optional().describe(
21818
21935
  "Flow name. Required for create. Optional for update (to rename) and duplicate."
21819
21936
  ),
21820
- content: z10.record(z10.string(), z10.unknown()).optional().describe("Flow.Json content. Used for create and update."),
21821
- patch: z10.boolean().optional().describe(
21937
+ content: z11.record(z11.string(), z11.unknown()).optional().describe("Flow.Json content. Used for create and update."),
21938
+ patch: z11.boolean().optional().describe(
21822
21939
  "Merge-patch for update (default true). When true, only provided fields are updated."
21823
21940
  ),
21824
- fields: z10.array(z10.string()).optional().describe("Dot-path selectors for get to return only specific fields."),
21825
- sort: z10.enum(["name", "updated_at", "created_at"]).optional().describe("Sort field for list."),
21826
- order: z10.enum(["asc", "desc"]).optional().describe("Sort order for list."),
21827
- includeDeleted: z10.boolean().optional().describe("Include soft-deleted flows in list results."),
21828
- cursor: z10.string().optional().describe(
21941
+ fields: z11.array(z11.string()).optional().describe("Dot-path selectors for get to return only specific fields."),
21942
+ sort: z11.enum(["name", "updated_at", "created_at"]).optional().describe("Sort field for list."),
21943
+ order: z11.enum(["asc", "desc"]).optional().describe("Sort order for list."),
21944
+ includeDeleted: z11.boolean().optional().describe("Include soft-deleted flows in list results."),
21945
+ cursor: z11.string().optional().describe(
21829
21946
  "Pagination cursor from a previous list response. Only used with the list action."
21830
21947
  ),
21831
- limit: z10.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action."),
21832
- previewId: z10.string().optional().describe(
21948
+ limit: z11.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action."),
21949
+ previewId: z11.string().optional().describe(
21833
21950
  "Preview ID (prv_...). Required for preview_get, preview_delete, and preview_regrant (all also require flowId)."
21834
21951
  ),
21835
- flowName: z10.string().optional().describe(
21952
+ flowName: z11.string().optional().describe(
21836
21953
  "Used by preview_create \u2014 provide one of flowName or flowSettingsId (preview_create also requires flowId)."
21837
21954
  ),
21838
- flowSettingsId: z10.string().optional().describe(
21955
+ flowSettingsId: z11.string().optional().describe(
21839
21956
  "Used by preview_create \u2014 provide one of flowName or flowSettingsId (preview_create also requires flowId)."
21840
21957
  ),
21841
- source: z10.discriminatedUnion("kind", [
21842
- z10.object({ kind: z10.literal("draft") }),
21843
- z10.object({
21844
- kind: z10.literal("deployment-version"),
21845
- deploymentVersionId: z10.string()
21958
+ source: z11.discriminatedUnion("kind", [
21959
+ z11.object({ kind: z11.literal("draft") }),
21960
+ z11.object({
21961
+ kind: z11.literal("deployment-version"),
21962
+ deploymentVersionId: z11.string()
21846
21963
  })
21847
21964
  ]).optional().describe(
21848
21965
  "What the preview should run, for preview_create: the flow's draft (default) or a deployed version's stored config (kind 'deployment-version' with its deploymentVersionId)."
21849
21966
  ),
21850
- siteUrl: z10.string().optional().describe(
21967
+ siteUrl: z11.string().optional().describe(
21851
21968
  "Optional site URL (e.g. https://shop.example.com) for preview_create. When provided, the activation grant is minted for that origin so the returned activationUrl works there."
21852
21969
  ),
21853
- origins: z10.array(z10.string()).optional().describe(
21970
+ origins: z11.array(z11.string()).optional().describe(
21854
21971
  "Site origins (bare https://host[:port]) to mint a preview activation grant for. Used by preview_regrant; the returned activationUrl targets the first origin."
21855
21972
  ),
21856
- sessionId: z10.string().optional().describe(
21973
+ sessionId: z11.string().optional().describe(
21857
21974
  "Observe session id \u2014 when set, preview_regrant mints an activation/forwarding grant PAIR: the returned activationUrl both activates the web preview and lets the page forward events to the session container (elbPreviewSession param). Pass it to observe server-side hops; omit for a plain web preview."
21858
21975
  )
21859
21976
  };
@@ -21879,9 +21996,6 @@ var PREVIEW_SUMMARY_FIELDS = [
21879
21996
  "createdBy",
21880
21997
  "createdAt"
21881
21998
  ];
21882
- function isRecord(value) {
21883
- return typeof value === "object" && value !== null && !Array.isArray(value);
21884
- }
21885
21999
  function redactPreview(data) {
21886
22000
  if (!isRecord(data)) return {};
21887
22001
  const out = {};
@@ -21920,6 +22034,8 @@ function createFlowManageToolSpec(client) {
21920
22034
  };
21921
22035
  }
21922
22036
  async function flowManageHandlerBody(client, input) {
22037
+ const parsed = parseToolInput(inputSchema11, input);
22038
+ if (!parsed.ok) return parsed.error;
21923
22039
  const {
21924
22040
  action,
21925
22041
  flowId,
@@ -21940,14 +22056,14 @@ async function flowManageHandlerBody(client, input) {
21940
22056
  siteUrl,
21941
22057
  origins,
21942
22058
  sessionId
21943
- } = input ?? {};
22059
+ } = parsed.data;
21944
22060
  const validationError = validateActionInput(
21945
22061
  "flow_manage",
21946
22062
  action ?? "",
21947
22063
  { flowId, projectId, name, previewId, flowName, flowSettingsId },
21948
22064
  FLOW_MANAGE_REQUIREMENTS
21949
22065
  );
21950
- if (validationError) return mcpError11(new Error(validationError));
22066
+ if (validationError) return mcpError13(new Error(validationError));
21951
22067
  try {
21952
22068
  switch (action) {
21953
22069
  case "list": {
@@ -21960,9 +22076,8 @@ async function flowManageHandlerBody(client, input) {
21960
22076
  cursor,
21961
22077
  limit
21962
22078
  });
21963
- const dataObj = data2;
21964
- const flows = dataObj.flows;
21965
- const safe2 = Array.isArray(flows) ? { ...dataObj, flows: flows.map(safeSummary) } : data2;
22079
+ const flows = isRecord(data2) ? data2.flows : void 0;
22080
+ const safe2 = isRecord(data2) && Array.isArray(flows) ? { ...data2, flows: flows.map(safeSummary) } : data2;
21966
22081
  return mcpResult12(safe2);
21967
22082
  }
21968
22083
  const data = await client.listAllFlows({
@@ -21987,16 +22102,14 @@ async function flowManageHandlerBody(client, input) {
21987
22102
  assertParam(flowId, "flowId", "get");
21988
22103
  const resolvedProjectId = resolveDefaultProject(client, projectId);
21989
22104
  if (!resolvedProjectId) {
21990
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22105
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
21991
22106
  }
21992
22107
  const flow = await client.getFlow({
21993
22108
  flowId,
21994
22109
  projectId: resolvedProjectId,
21995
22110
  fields
21996
22111
  });
21997
- const safe = safeDetail(
21998
- flow
21999
- );
22112
+ const safe = safeDetail(flow);
22000
22113
  const appUrl = flowPageUrl(client, resolvedProjectId, safe.id);
22001
22114
  return flowCanvasResult({
22002
22115
  flowId: safe.id,
@@ -22021,16 +22134,14 @@ async function flowManageHandlerBody(client, input) {
22021
22134
  assertParam(name, "name", "create");
22022
22135
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22023
22136
  if (!resolvedProjectId) {
22024
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22137
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22025
22138
  }
22026
22139
  const created = await client.createFlow({
22027
22140
  name,
22028
22141
  content: content ?? {},
22029
22142
  projectId: resolvedProjectId
22030
22143
  });
22031
- const safeCreated = safeDetail(
22032
- created
22033
- );
22144
+ const safeCreated = safeDetail(created);
22034
22145
  const createdAppUrl = flowPageUrl(
22035
22146
  client,
22036
22147
  resolvedProjectId,
@@ -22059,7 +22170,7 @@ async function flowManageHandlerBody(client, input) {
22059
22170
  assertParam(flowId, "flowId", "update");
22060
22171
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22061
22172
  if (!resolvedProjectId) {
22062
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22173
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22063
22174
  }
22064
22175
  const updated = await client.updateFlow({
22065
22176
  flowId,
@@ -22068,9 +22179,7 @@ async function flowManageHandlerBody(client, input) {
22068
22179
  content,
22069
22180
  mergePatch: patch ?? true
22070
22181
  });
22071
- const safeUpdated = safeDetail(
22072
- updated
22073
- );
22182
+ const safeUpdated = safeDetail(updated);
22074
22183
  return flowCanvasResult({
22075
22184
  flowId: safeUpdated.id,
22076
22185
  configName: safeUpdated.name ?? "default",
@@ -22093,7 +22202,7 @@ async function flowManageHandlerBody(client, input) {
22093
22202
  assertParam(flowId, "flowId", "delete");
22094
22203
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22095
22204
  if (!resolvedProjectId) {
22096
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22205
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22097
22206
  }
22098
22207
  const deleted = await client.deleteFlow({
22099
22208
  flowId,
@@ -22105,22 +22214,20 @@ async function flowManageHandlerBody(client, input) {
22105
22214
  assertParam(flowId, "flowId", "duplicate");
22106
22215
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22107
22216
  if (!resolvedProjectId) {
22108
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22217
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22109
22218
  }
22110
22219
  const duplicated = await client.duplicateFlow({
22111
22220
  flowId,
22112
22221
  name,
22113
22222
  projectId: resolvedProjectId
22114
22223
  });
22115
- return mcpResult12(
22116
- safeDetail(duplicated)
22117
- );
22224
+ return mcpResult12(safeDetail(duplicated).fields);
22118
22225
  }
22119
22226
  case "preview_list": {
22120
22227
  assertParam(flowId, "flowId", "preview_list");
22121
22228
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22122
22229
  if (!resolvedProjectId) {
22123
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22230
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22124
22231
  }
22125
22232
  const data = await client.listPreviews({
22126
22233
  projectId: resolvedProjectId,
@@ -22133,7 +22240,7 @@ async function flowManageHandlerBody(client, input) {
22133
22240
  assertParam(previewId, "previewId", "preview_get");
22134
22241
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22135
22242
  if (!resolvedProjectId) {
22136
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22243
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22137
22244
  }
22138
22245
  const data = await client.getPreview({
22139
22246
  projectId: resolvedProjectId,
@@ -22146,7 +22253,7 @@ async function flowManageHandlerBody(client, input) {
22146
22253
  assertParam(flowId, "flowId", "preview_create");
22147
22254
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22148
22255
  if (!resolvedProjectId) {
22149
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22256
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22150
22257
  }
22151
22258
  const preview = await client.createPreview({
22152
22259
  projectId: resolvedProjectId,
@@ -22168,7 +22275,7 @@ async function flowManageHandlerBody(client, input) {
22168
22275
  assertParam(previewId, "previewId", "preview_delete");
22169
22276
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22170
22277
  if (!resolvedProjectId) {
22171
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22278
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22172
22279
  }
22173
22280
  const data = await client.deletePreview({
22174
22281
  projectId: resolvedProjectId,
@@ -22184,10 +22291,10 @@ async function flowManageHandlerBody(client, input) {
22184
22291
  assertParam(previewId, "previewId", "preview_regrant");
22185
22292
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22186
22293
  if (!resolvedProjectId) {
22187
- return mcpError11(new Error(NO_DEFAULT_PROJECT_ERROR));
22294
+ return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22188
22295
  }
22189
22296
  if (!client.regrantPreview) {
22190
- return mcpError11(
22297
+ return mcpError13(
22191
22298
  new Error("preview_regrant is not supported by this client")
22192
22299
  );
22193
22300
  }
@@ -22211,7 +22318,7 @@ async function flowManageHandlerBody(client, input) {
22211
22318
  );
22212
22319
  }
22213
22320
  } catch (error) {
22214
- return mcpError11(error, isAuthError(error) ? AUTH_HINT : void 0);
22321
+ return mcpError13(error, isAuthError(error) ? AUTH_HINT : void 0);
22215
22322
  }
22216
22323
  }
22217
22324
  function registerFlowManageTool(server, client) {
@@ -22224,15 +22331,13 @@ function registerFlowManageTool(server, client) {
22224
22331
  inputSchema: spec.inputSchema,
22225
22332
  annotations: spec.annotations
22226
22333
  },
22227
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
22228
- // type-erased (input: unknown) => Promise<unknown> form by design.
22229
- spec.handler
22334
+ (args) => flowManageHandlerBody(client, args)
22230
22335
  );
22231
22336
  }
22232
22337
 
22233
22338
  // src/tools/deploy-manage.ts
22234
- import { z as z11 } from "zod";
22235
- import { mcpResult as mcpResult13, mcpError as mcpError12 } from "@walkeros/core";
22339
+ import { z as z12 } from "zod";
22340
+ import { mcpResult as mcpResult13, mcpError as mcpError14 } from "@walkeros/core";
22236
22341
 
22237
22342
  // src/tools/_resolvers.ts
22238
22343
  var DeploymentNotFoundError = class extends Error {
@@ -22288,24 +22393,24 @@ async function resolveDeploymentSlug(args) {
22288
22393
  var TITLE12 = "Deploy Management";
22289
22394
  var DESCRIPTION11 = "Deploy walkerOS flows and manage deployments. deploy waits for the deployment to reach a terminal status by default (wait=true), with a 12-minute budget; pass wait=false to return immediately with the deployment id. A finished deployment carries its status and, on failure, an errorMessage with the user-facing reason; use the get action to re-read it. list supports cursor and limit for pagination. delete removes an active deployment. For get and delete pass flowId (required) plus optional slug to disambiguate when a flow has multiple active deployments. If a flow has >=2 active deployments and no slug is supplied, the tool returns a MULTIPLE_DEPLOYMENTS error with a details[] list showing each deployment's slug, type, status, and updatedAt.";
22290
22395
  var inputSchema12 = {
22291
- action: z11.enum(["deploy", "list", "get", "delete"]).describe("Deployment action to perform"),
22292
- projectId: z11.string().optional().describe("Project ID. Optional; falls back to the default project."),
22293
- flowId: z11.string().optional().describe("Required for deploy, get, delete."),
22294
- slug: z11.string().optional().describe(
22396
+ action: z12.enum(["deploy", "list", "get", "delete"]).describe("Deployment action to perform"),
22397
+ projectId: z12.string().optional().describe("Project ID. Optional; falls back to the default project."),
22398
+ flowId: z12.string().optional().describe("Required for deploy, get, delete."),
22399
+ slug: z12.string().optional().describe(
22295
22400
  "Deployment slug. Optional disambiguator for get/delete when the flow has multiple active deployments."
22296
22401
  ),
22297
- type: z11.enum(["web", "server"]).optional().describe("Deployment type filter for list."),
22298
- status: z11.string().optional().describe("Status filter for list."),
22299
- wait: z11.boolean().optional().describe(
22402
+ type: z12.enum(["web", "server"]).optional().describe("Deployment type filter for list."),
22403
+ status: z12.string().optional().describe("Status filter for list."),
22404
+ wait: z12.boolean().optional().describe(
22300
22405
  "Wait for the deployment to reach a terminal status (default true), with a 12-minute budget. Set false to return the deployment id immediately. Only used with deploy action."
22301
22406
  ),
22302
- flowName: z11.string().optional().describe(
22407
+ flowName: z12.string().optional().describe(
22303
22408
  "Flow name for multi-settings flows. Only used with deploy action."
22304
22409
  ),
22305
- cursor: z11.string().optional().describe(
22410
+ cursor: z12.string().optional().describe(
22306
22411
  "Pagination cursor from a previous list response. Only used with the list action."
22307
22412
  ),
22308
- limit: z11.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action.")
22413
+ limit: z12.number().int().min(1).max(100).optional().describe("Max items per page (1-100). Only used with the list action.")
22309
22414
  };
22310
22415
  var annotations12 = {
22311
22416
  readOnlyHint: false,
@@ -22340,13 +22445,22 @@ function withAppUrl(data, appUrl) {
22340
22445
  }
22341
22446
  return { ...data, appUrl };
22342
22447
  }
22448
+ function readDeploymentSummary(value) {
22449
+ if (!isRecord(value)) return [];
22450
+ const slug = stringField(value, "slug");
22451
+ const type = stringField(value, "type");
22452
+ const status = stringField(value, "status");
22453
+ const updatedAt = stringField(value, "updatedAt");
22454
+ return slug !== void 0 && type !== void 0 && status !== void 0 && updatedAt !== void 0 ? [{ ...value, slug, type, status, updatedAt }] : [];
22455
+ }
22343
22456
  function listForResolver(client, projectId) {
22344
22457
  return async (q) => {
22345
22458
  const resp = await client.listDeployments({
22346
22459
  projectId: projectId || q.projectId || void 0,
22347
22460
  flowId: q.flowId
22348
22461
  });
22349
- return resp.deployments ?? [];
22462
+ const deployments = isRecord(resp) ? resp.deployments : void 0;
22463
+ return Array.isArray(deployments) ? deployments.flatMap(readDeploymentSummary) : [];
22350
22464
  };
22351
22465
  }
22352
22466
  function createDeployManageToolSpec(client) {
@@ -22360,6 +22474,8 @@ function createDeployManageToolSpec(client) {
22360
22474
  };
22361
22475
  }
22362
22476
  async function deployManageHandlerBody(client, input) {
22477
+ const parsed = parseToolInput(inputSchema12, input);
22478
+ if (!parsed.ok) return parsed.error;
22363
22479
  const {
22364
22480
  action,
22365
22481
  projectId,
@@ -22371,14 +22487,14 @@ async function deployManageHandlerBody(client, input) {
22371
22487
  flowName,
22372
22488
  cursor,
22373
22489
  limit
22374
- } = input ?? {};
22490
+ } = parsed.data;
22375
22491
  const validationError = validateActionInput(
22376
22492
  "deploy_manage",
22377
22493
  action ?? "",
22378
22494
  { flowId, projectId },
22379
22495
  DEPLOY_MANAGE_REQUIREMENTS
22380
22496
  );
22381
- if (validationError) return mcpError12(new Error(validationError));
22497
+ if (validationError) return mcpError14(new Error(validationError));
22382
22498
  try {
22383
22499
  switch (action) {
22384
22500
  case "deploy": {
@@ -22437,7 +22553,7 @@ async function deployManageHandlerBody(client, input) {
22437
22553
  return mcpResult13(
22438
22554
  redactDisplayNames({
22439
22555
  deleted: true,
22440
- ...data
22556
+ ...fieldsOf(data)
22441
22557
  })
22442
22558
  );
22443
22559
  }
@@ -22447,7 +22563,7 @@ async function deployManageHandlerBody(client, input) {
22447
22563
  );
22448
22564
  }
22449
22565
  } catch (error) {
22450
- return mcpError12(error, isAuthError(error) ? AUTH_HINT : void 0);
22566
+ return mcpError14(error, isAuthError(error) ? AUTH_HINT : void 0);
22451
22567
  }
22452
22568
  }
22453
22569
  function registerDeployTool(server, client) {
@@ -22460,35 +22576,33 @@ function registerDeployTool(server, client) {
22460
22576
  inputSchema: spec.inputSchema,
22461
22577
  annotations: spec.annotations
22462
22578
  },
22463
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
22464
- // type-erased (input: unknown) => Promise<unknown> form by design.
22465
- spec.handler
22579
+ (args) => deployManageHandlerBody(client, args)
22466
22580
  );
22467
22581
  }
22468
22582
 
22469
22583
  // src/tools/secret-manage.ts
22470
- import { z as z12 } from "zod";
22471
- import { mcpResult as mcpResult14, mcpError as mcpError13 } from "@walkeros/core";
22584
+ import { z as z13 } from "zod";
22585
+ import { mcpResult as mcpResult14, mcpError as mcpError15 } from "@walkeros/core";
22472
22586
  var TITLE13 = "Secret Management";
22473
22587
  var DESCRIPTION12 = "Manage a flow\u2019s managed secrets (the $secret.<NAME> values its steps reference at deploy/run time). Actions: list (metadata only), set (create), update (rotate value), delete. Secrets are write-mostly: values are encrypted at rest and are NEVER returned, listed, or echoed. Reference a secret from a flow step as $secret.<NAME>. Credentials, tokens, and private keys must use $secret, not $env: the deploy pipeline only injects values referenced as $secret.<NAME> into the server runner. Server flows only.";
22474
22588
  var inputSchema13 = {
22475
- action: z12.enum(["list", "set", "update", "delete"]).describe("Secret management action to perform"),
22476
- projectId: z12.string().optional().describe(
22589
+ action: z13.enum(["list", "set", "update", "delete"]).describe("Secret management action to perform"),
22590
+ projectId: z13.string().optional().describe(
22477
22591
  "Project ID. Optional: falls back to the default project when omitted."
22478
22592
  ),
22479
- flowId: z12.string().describe(
22593
+ flowId: z13.string().describe(
22480
22594
  "Flow ID (flow_...) or config ID (cfg_...). Required for list, set, update, and delete: secrets are flow-scoped."
22481
22595
  ),
22482
- name: z12.string().regex(
22596
+ name: z13.string().regex(
22483
22597
  /^[A-Z_][A-Z0-9_]*$/,
22484
22598
  "Secret name must be uppercase letters, digits, and underscores, starting with a letter or underscore."
22485
22599
  ).optional().describe(
22486
22600
  "Secret name (UPPER_SNAKE_CASE). Required for set. Referenced in flows as $secret.<NAME>."
22487
22601
  ),
22488
- value: z12.string().min(1).max(65536).optional().describe(
22602
+ value: z13.string().min(1).max(65536).optional().describe(
22489
22603
  "Secret value (1-65536 chars). Required for set and update. Write-only: never returned or logged."
22490
22604
  ),
22491
- secretId: z12.string().optional().describe(
22605
+ secretId: z13.string().optional().describe(
22492
22606
  'Secret ID (sec_...). Required for update and delete. Use action "list" to find it.'
22493
22607
  )
22494
22608
  };
@@ -22509,18 +22623,20 @@ function createSecretManageToolSpec(client) {
22509
22623
  };
22510
22624
  }
22511
22625
  async function secretManageHandlerBody(client, input) {
22512
- const { action, projectId, flowId, name, value, secretId } = input ?? {};
22626
+ const parsed = parseToolInput(inputSchema13, input);
22627
+ if (!parsed.ok) return parsed.error;
22628
+ const { action, projectId, flowId, name, value, secretId } = parsed.data;
22513
22629
  const validationError = validateActionInput(
22514
22630
  "secret_manage",
22515
22631
  action ?? "",
22516
22632
  { flowId, name, value, secretId },
22517
22633
  SECRET_MANAGE_REQUIREMENTS
22518
22634
  );
22519
- if (validationError) return mcpError13(new Error(validationError));
22635
+ if (validationError) return mcpError15(new Error(validationError));
22520
22636
  try {
22521
22637
  const resolvedProjectId = resolveDefaultProject(client, projectId);
22522
22638
  if (!resolvedProjectId)
22523
- return mcpError13(new Error(NO_DEFAULT_PROJECT_ERROR));
22639
+ return mcpError15(new Error(NO_DEFAULT_PROJECT_ERROR));
22524
22640
  switch (action) {
22525
22641
  case "list": {
22526
22642
  assertParam(flowId, "flowId", "list");
@@ -22579,7 +22695,7 @@ async function secretManageHandlerBody(client, input) {
22579
22695
  );
22580
22696
  }
22581
22697
  } catch (error) {
22582
- return mcpError13(error, isAuthError(error) ? AUTH_HINT : void 0);
22698
+ return mcpError15(error, isAuthError(error) ? AUTH_HINT : void 0);
22583
22699
  }
22584
22700
  }
22585
22701
  function registerSecretManageTool(server, client) {
@@ -22592,22 +22708,20 @@ function registerSecretManageTool(server, client) {
22592
22708
  inputSchema: spec.inputSchema,
22593
22709
  annotations: spec.annotations
22594
22710
  },
22595
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
22596
- // type-erased (input: unknown) => Promise<unknown> form by design.
22597
- spec.handler
22711
+ (args) => secretManageHandlerBody(client, args)
22598
22712
  );
22599
22713
  }
22600
22714
 
22601
22715
  // src/tools/observe-journeys.ts
22602
- import { z as z13 } from "zod";
22603
- import { mcpResult as mcpResult15, mcpError as mcpError14 } from "@walkeros/core";
22716
+ import { z as z14 } from "zod";
22717
+ import { mcpResult as mcpResult15, mcpError as mcpError16 } from "@walkeros/core";
22604
22718
  var TITLE14 = "Observe Journeys";
22605
22719
  var DESCRIPTION13 = "Read the assembled, cross-runtime journeys for a flow that is currently being observed (an active Observe session). Pass flowId; the active session is resolved for you (a flow has at most one). Each journey is one traced event reconstructed end to end across web and server: its ordered hops (source, transformer, collector, destination), each hop status (pending/done/skipped/error), captured in/out payloads, consent, and vendor calls. Use this to see what actually happened to live events: which destinations fired, what mapping ran, where an event was skipped or errored, and whether records were lost (gaps + a journey `lossy` flag). When the flow has no active session the result is `{ sessionId: null, journeys: [], gaps: [] }` \u2014 start an Observe session in the app and drive traffic first, then read again. Narrow with traceId (one trace) and limit (page cap, most recent kept; default 50). Read-only.";
22606
22720
  var inputSchema14 = {
22607
- flowId: z13.string().describe("Flow to read journeys for (its active Observe session)."),
22608
- projectId: z13.string().optional().describe("Project ID. Optional; falls back to the default project."),
22609
- traceId: z13.string().optional().describe("Return only the journeys for this trace id."),
22610
- limit: z13.number().int().min(1).max(100).optional().describe(
22721
+ flowId: z14.string().describe("Flow to read journeys for (its active Observe session)."),
22722
+ projectId: z14.string().optional().describe("Project ID. Optional; falls back to the default project."),
22723
+ traceId: z14.string().optional().describe("Return only the journeys for this trace id."),
22724
+ limit: z14.number().int().min(1).max(100).optional().describe(
22611
22725
  "Max journeys to return (1-100, most recent kept). Defaults to 50."
22612
22726
  )
22613
22727
  };
@@ -22665,9 +22779,11 @@ function createObserveJourneysToolSpec(client) {
22665
22779
  };
22666
22780
  }
22667
22781
  async function observeJourneysHandlerBody(client, input) {
22668
- const { flowId, projectId, traceId, limit } = input ?? {};
22782
+ const parsed = parseToolInput(inputSchema14, input);
22783
+ if (!parsed.ok) return parsed.error;
22784
+ const { flowId, projectId, traceId, limit } = parsed.data;
22669
22785
  if (!flowId) {
22670
- return mcpError14(new Error("flowId is required for observe_journeys."));
22786
+ return mcpError16(new Error("flowId is required for observe_journeys."));
22671
22787
  }
22672
22788
  try {
22673
22789
  const result = await client.listJourneys({
@@ -22694,7 +22810,7 @@ async function observeJourneysHandlerBody(client, input) {
22694
22810
  ]
22695
22811
  });
22696
22812
  } catch (error) {
22697
- return mcpError14(error, isAuthError(error) ? AUTH_HINT : void 0);
22813
+ return mcpError16(error, isAuthError(error) ? AUTH_HINT : void 0);
22698
22814
  }
22699
22815
  }
22700
22816
  function registerObserveJourneysTool(server, client) {
@@ -22707,15 +22823,13 @@ function registerObserveJourneysTool(server, client) {
22707
22823
  inputSchema: spec.inputSchema,
22708
22824
  annotations: spec.annotations
22709
22825
  },
22710
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
22711
- // type-erased (input: unknown) => Promise<unknown> form by design.
22712
- spec.handler
22826
+ (args) => observeJourneysHandlerBody(client, args)
22713
22827
  );
22714
22828
  }
22715
22829
 
22716
22830
  // src/tools/observe-session.ts
22717
- import { z as z14 } from "zod";
22718
- import { mcpResult as mcpResult16, mcpError as mcpError15 } from "@walkeros/core";
22831
+ import { z as z15 } from "zod";
22832
+ import { mcpResult as mcpResult16, mcpError as mcpError17 } from "@walkeros/core";
22719
22833
  var TITLE15 = "Observe Session";
22720
22834
  var DESCRIPTION14 = "Open, inspect, or end an Observe session: a time-boxed window on one flow that runtimes attach to as arms. A preview arm streams from a browser, a container arm runs server-side, and both feed ONE shared journeys feed. start opens the window (arms picks which runtimes attach), status reports per-arm state plus recordsReceived and expiresAt, stop ends the whole session including every arm. A flow has at most one session, so status/stop resolve it from flowId when sessionId is omitted. Read the events with observe_journeys; this tool never returns event data and never judges whether events are correct.";
22721
22835
  var HINT_SIMULATE_FIRST = "Simulate before preview: flow_simulate checks mapping with no browser.";
@@ -22726,29 +22840,29 @@ var HINT_EMPTY_FEED = "recordsReceived is 0: nothing has reached the feed yet. D
22726
22840
  var HINT_ENDED = "Session ended and both arms detached. Start a new one with observe_session start.";
22727
22841
  var HINT_NO_WINDOW = "No Observe session on this flow. Open one with observe_session start.";
22728
22842
  var inputSchema15 = {
22729
- action: z14.enum(["start", "status", "stop"]).describe(
22843
+ action: z15.enum(["start", "status", "stop"]).describe(
22730
22844
  "start opens a session, status reports arm state, stop ends the whole session."
22731
22845
  ),
22732
- flowId: z14.string().describe("Flow the Observe session runs on."),
22733
- projectId: z14.string().optional().describe("Project ID. Optional; falls back to the default project."),
22734
- sessionId: z14.string().optional().describe(
22846
+ flowId: z15.string().describe("Flow the Observe session runs on."),
22847
+ projectId: z15.string().optional().describe("Project ID. Optional; falls back to the default project."),
22848
+ sessionId: z15.string().optional().describe(
22735
22849
  "Session to act on for status/stop. Optional; the flow has at most one session and it is resolved for you."
22736
22850
  ),
22737
- arms: z14.object({
22738
- container: z14.literal(true).optional().describe(
22851
+ arms: z15.object({
22852
+ container: z15.literal(true).optional().describe(
22739
22853
  "Pass true to attach the server container arm, which selects the flow's server settings when no preview arm is named. Only true is accepted: a web settings that references a server flow always brings its container arm along, so a container cannot be suppressed here."
22740
22854
  ),
22741
- preview: z14.string().optional().describe(
22855
+ preview: z15.string().optional().describe(
22742
22856
  "Name the flow settings this session observes. A web settings attaches the browser preview arm (plus the container arm of any server flow it references); a server settings attaches the container arm alone. Omit to use the flow's single web settings."
22743
22857
  )
22744
22858
  }).optional().describe(
22745
22859
  "Which runtimes attach. Omit to attach the default preview arm; a web settings that references a server flow brings its container arm with it."
22746
22860
  ),
22747
- origins: z14.array(z14.string()).optional().describe(
22861
+ origins: z15.array(z15.string()).optional().describe(
22748
22862
  "Bare https origins (https://host[:port]) the session may ingest web events from."
22749
22863
  ),
22750
- level: z14.enum(["off", "standard", "trace"]).optional().describe("Container observation verbosity. Defaults to the app's own."),
22751
- replace: z14.boolean().optional().describe(
22864
+ level: z15.enum(["off", "standard", "trace"]).optional().describe("Container observation verbosity. Defaults to the app's own."),
22865
+ replace: z15.boolean().optional().describe(
22752
22866
  "Replace the flow's existing window instead of attaching to it. Re-provisions from the new config."
22753
22867
  )
22754
22868
  };
@@ -22759,14 +22873,14 @@ var annotations15 = {
22759
22873
  openWorldHint: true
22760
22874
  };
22761
22875
  function readSettings(flow) {
22762
- if (!flow || typeof flow !== "object") return [];
22876
+ if (!isRecord(flow)) return [];
22763
22877
  const settings = flow.settings;
22764
22878
  if (!Array.isArray(settings)) return [];
22765
22879
  const entries = [];
22766
22880
  for (const entry of settings) {
22767
- if (!entry || typeof entry !== "object") continue;
22768
- const { name, platform } = entry;
22769
- if (typeof name !== "string") continue;
22881
+ const name = stringField(entry, "name");
22882
+ const platform = stringField(entry, "platform");
22883
+ if (name === void 0) continue;
22770
22884
  if (platform !== "web" && platform !== "server") continue;
22771
22885
  entries.push({ name, platform });
22772
22886
  }
@@ -22896,6 +23010,8 @@ function createObserveSessionToolSpec(client) {
22896
23010
  };
22897
23011
  }
22898
23012
  async function observeSessionHandlerBody(client, input) {
23013
+ const parsed = parseToolInput(inputSchema15, input);
23014
+ if (!parsed.ok) return parsed.error;
22899
23015
  const {
22900
23016
  action,
22901
23017
  flowId,
@@ -22905,19 +23021,19 @@ async function observeSessionHandlerBody(client, input) {
22905
23021
  origins,
22906
23022
  level,
22907
23023
  replace
22908
- } = input ?? {};
23024
+ } = parsed.data;
22909
23025
  if (!flowId) {
22910
- return mcpError15(new Error("flowId is required for observe_session."));
23026
+ return mcpError17(new Error("flowId is required for observe_session."));
22911
23027
  }
22912
23028
  const resolvedProjectId = resolveProjectId(client, projectId);
22913
23029
  if (!resolvedProjectId) {
22914
- return mcpError15(new Error(NO_DEFAULT_PROJECT_ERROR2));
23030
+ return mcpError17(new Error(NO_DEFAULT_PROJECT_ERROR2));
22915
23031
  }
22916
23032
  try {
22917
23033
  switch (action) {
22918
23034
  case "start": {
22919
23035
  if (!client.startObserveSession) {
22920
- return mcpError15(
23036
+ return mcpError17(
22921
23037
  new Error("observe_session start is not supported by this client.")
22922
23038
  );
22923
23039
  }
@@ -22943,7 +23059,7 @@ async function observeSessionHandlerBody(client, input) {
22943
23059
  }
22944
23060
  case "status": {
22945
23061
  if (!client.getObserveSession) {
22946
- return mcpError15(
23062
+ return mcpError17(
22947
23063
  new Error(
22948
23064
  "observe_session status is not supported by this client."
22949
23065
  )
@@ -22970,7 +23086,7 @@ async function observeSessionHandlerBody(client, input) {
22970
23086
  }
22971
23087
  case "stop": {
22972
23088
  if (!client.endObserveSession) {
22973
- return mcpError15(
23089
+ return mcpError17(
22974
23090
  new Error("observe_session stop is not supported by this client.")
22975
23091
  );
22976
23092
  }
@@ -23000,7 +23116,7 @@ async function observeSessionHandlerBody(client, input) {
23000
23116
  );
23001
23117
  }
23002
23118
  } catch (error) {
23003
- return mcpError15(error, isAuthError(error) ? AUTH_HINT : void 0);
23119
+ return mcpError17(error, isAuthError(error) ? AUTH_HINT : void 0);
23004
23120
  }
23005
23121
  }
23006
23122
  function registerObserveSessionTool(server, client) {
@@ -23013,15 +23129,13 @@ function registerObserveSessionTool(server, client) {
23013
23129
  inputSchema: spec.inputSchema,
23014
23130
  annotations: spec.annotations
23015
23131
  },
23016
- // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
23017
- // type-erased (input: unknown) => Promise<unknown> form by design.
23018
- spec.handler
23132
+ (args) => observeSessionHandlerBody(client, args)
23019
23133
  );
23020
23134
  }
23021
23135
 
23022
23136
  // src/tools/hub-manage.ts
23023
- import { z as z15 } from "zod";
23024
- import { mcpResult as mcpResult17, mcpError as mcpError16 } from "@walkeros/core";
23137
+ import { z as z16 } from "zod";
23138
+ import { mcpResult as mcpResult17, mcpError as mcpError18 } from "@walkeros/core";
23025
23139
 
23026
23140
  // src/tools/feature-gate.ts
23027
23141
  var FEATURE_NOT_AVAILABLE = "FEATURE_NOT_AVAILABLE";
@@ -23074,56 +23188,56 @@ var TOOL_ANCHOR_TYPES = [
23074
23188
  "tag"
23075
23189
  ];
23076
23190
  var HUB_MANAGE_INPUT_SCHEMA = {
23077
- action: z15.enum(ACTIONS, {
23191
+ action: z16.enum(ACTIONS, {
23078
23192
  error: () => `Unknown action. Use one of: ${ACTIONS.join(", ")}`
23079
23193
  }).describe("Which part of the release history to read or write"),
23080
- projectId: z15.string().optional().describe(
23194
+ projectId: z16.string().optional().describe(
23081
23195
  "Project ID. Optional: falls back to the default project when omitted."
23082
23196
  ),
23083
- flowId: z15.string().optional().describe(
23197
+ flowId: z16.string().optional().describe(
23084
23198
  'Flow ID (flow_...). Required for every action except "knowledge", which hangs on a page rather than a flow and refuses this field.'
23085
23199
  ),
23086
- versionId: z15.string().min(1).optional().describe(
23200
+ versionId: z16.string().min(1).optional().describe(
23087
23201
  'Release version ID (ver_...) from action "releases". Addresses one release for release_get and rationale_set. Pass this or versionNumber.'
23088
23202
  ),
23089
- versionNumber: z15.number().int().positive().optional().describe(
23203
+ versionNumber: z16.number().int().positive().optional().describe(
23090
23204
  'Spine release number for this flow, the `versionNumber` field of action "releases". Alternative to versionId. Not the same as a row\'s deploymentAttempt.'
23091
23205
  ),
23092
- step: z15.string().optional().describe(
23206
+ step: z16.string().optional().describe(
23093
23207
  'Step key as "type.name", e.g. "destination.ga4" or "contract.checkout". Required for step_history.'
23094
23208
  ),
23095
- flow: z15.string().optional().describe(
23209
+ flow: z16.string().optional().describe(
23096
23210
  'Named flow inside the config, e.g. "web" or "server". Optional for step_history: omit to scan every named flow. Ignored for contract steps, which are top-level.'
23097
23211
  ),
23098
- text: z15.string().trim().min(1).max(MAX_ANNOTATION_TEXT_LENGTH).optional().describe(
23212
+ text: z16.string().trim().min(1).max(MAX_ANNOTATION_TEXT_LENGTH).optional().describe(
23099
23213
  `The text to write (1-${MAX_ANNOTATION_TEXT_LENGTH} chars). Required for rationale_set and note_add. As rationale it replaces the note already on the release and never touches the machine summary; as a note it is appended to a thread and nothing is ever replaced.`
23100
23214
  ),
23101
- limit: z15.number().int().positive().optional().describe(
23215
+ limit: z16.number().int().positive().optional().describe(
23102
23216
  `Page size. Releases to list for action "releases" (max 100), or releases to SCAN for step_history (max ${MAX_STEP_HISTORY_LIMIT}). For step_history this bounds releases, not entries: a step present in several named flows yields one entry per flow per release, and the scan stops at ${MAX_STEP_HISTORY_ENTRIES} entries with entriesTruncated set. Narrow with "flow" to avoid that. For threads it bounds threads, and a read that carries messages is held to ${MAX_THREADS_WITH_MESSAGES} of them. Knowledge entries are bounded the same way.`
23103
23217
  ),
23104
- offset: z15.number().int().min(0).optional().describe('Releases to skip. Action "releases" only.'),
23105
- anchorType: z15.enum(TOOL_ANCHOR_TYPES).optional().describe(
23218
+ offset: z16.number().int().min(0).optional().describe('Releases to skip. Action "releases" only.'),
23219
+ anchorType: z16.enum(TOOL_ANCHOR_TYPES).optional().describe(
23106
23220
  'What a thread hangs on. Defaults to "release", the only anchor the app writes today. Pair it with anchorKey; a key means a different thing under each type.'
23107
23221
  ),
23108
- anchorKey: z15.string().min(1).max(MAX_ANCHOR_KEY_LENGTH).optional().describe(
23222
+ anchorKey: z16.string().min(1).max(MAX_ANCHOR_KEY_LENGTH).optional().describe(
23109
23223
  'What the anchor addresses within its type: a release version ID (ver_...) for "release", a "type.name" step key for "step". For a release you can pass versionId or versionNumber instead. Omit entirely on action "threads" to read every thread on the flow.'
23110
23224
  ),
23111
- anchorLabel: z15.string().min(1).max(MAX_ANCHOR_LABEL_LENGTH).optional().describe(
23225
+ anchorLabel: z16.string().min(1).max(MAX_ANCHOR_LABEL_LENGTH).optional().describe(
23112
23226
  'How the anchor reads on screen, stored once when a thread is opened so a later rename leaves it readable. Derived for a release ("v14"); pass it only when opening a thread on another anchor type.'
23113
23227
  ),
23114
- threadId: z15.string().min(1).optional().describe(
23228
+ threadId: z16.string().min(1).optional().describe(
23115
23229
  'Thread ID (thr_...) from action "threads". Pass it to note_add to reply in that thread; omit it to open a new thread on the anchor.'
23116
23230
  ),
23117
- status: z15.enum(THREAD_STATUSES).optional().describe(
23231
+ status: z16.enum(THREAD_STATUSES).optional().describe(
23118
23232
  'Read only threads in this state. Action "threads" only; omit for both.'
23119
23233
  ),
23120
- pageKey: z15.string().min(1).max(MAX_SOURCE_KEY_LENGTH).optional().describe(
23234
+ pageKey: z16.string().min(1).max(MAX_SOURCE_KEY_LENGTH).optional().describe(
23121
23235
  'The page a note was left on, as Tag Mode addressed it, usually the page URL. Narrows action "knowledge" to every frame that page holds, at any depth; omit it to read the whole project.'
23122
23236
  ),
23123
- frameId: z15.string().regex(FRAME_ID_PATTERN).optional().describe(
23237
+ frameId: z16.string().regex(FRAME_ID_PATTERN).optional().describe(
23124
23238
  'One frame (frm_...), the named rectangle a note hangs on. Action "knowledge" only. Narrower than pageKey, since a page holds several frames.'
23125
23239
  ),
23126
- markId: z15.string().min(1).max(MAX_MARK_ID_LENGTH).optional().describe(
23240
+ markId: z16.string().min(1).max(MAX_MARK_ID_LENGTH).optional().describe(
23127
23241
  'One tag within "frameId", by its tag id (the id of an entry in frame_manage marks.tags). Action "knowledge" only, and refused without frameId, since a mark id alone addresses nothing. Naming a mark is also what attaches the message bodies.'
23128
23242
  )
23129
23243
  };
@@ -23169,7 +23283,7 @@ var HUB_HINT_ENTRY_NAMES_FLOW = "An entry names the flow it was written against
23169
23283
  var HUB_HINT_KNOWLEDGE_READ_ONLY = "This tool only reads knowledge. Answering a note, and settling it, are done by a person in the app.";
23170
23284
  var HUB_HINT_READ_FRAME = 'Use frame_manage action "get" with the frameId to read the frame and its marks.';
23171
23285
  var HUB_NOT_FOUND_HINT = 'Use action "releases" to find version ids and action "threads" to find thread ids.';
23172
- var hubInputSchema = z15.object(HUB_MANAGE_INPUT_SCHEMA);
23286
+ var hubInputSchema = z16.object(HUB_MANAGE_INPUT_SCHEMA);
23173
23287
  var HubToolError = class extends Error {
23174
23288
  constructor(code, message) {
23175
23289
  super(message);
@@ -23589,12 +23703,7 @@ async function hubManageHandler(client, rawInput) {
23589
23703
  try {
23590
23704
  const parsed = hubInputSchema.safeParse(rawInput ?? {});
23591
23705
  if (!parsed.success) {
23592
- throw new HubToolError(
23593
- "INVALID_INPUT",
23594
- parsed.error.issues.map(
23595
- (issue) => `${issue.path.join(".") || "input"}: ${issue.message}`
23596
- ).join("; ")
23597
- );
23706
+ throw new HubToolError("INVALID_INPUT", inputIssuesMessage(parsed.error));
23598
23707
  }
23599
23708
  const input = parsed.data;
23600
23709
  const { action } = input;
@@ -23627,7 +23736,7 @@ async function hubManageHandler(client, rawInput) {
23627
23736
  return await handleNoteAdd(client, projectId, flowId, input);
23628
23737
  }
23629
23738
  } catch (error) {
23630
- return mcpError16(error, errorHint(error, "hub", HUB_NOT_FOUND_HINT));
23739
+ return mcpError18(error, errorHint(error, "hub", HUB_NOT_FOUND_HINT));
23631
23740
  }
23632
23741
  }
23633
23742
  function createHubManageToolSpec(client) {
@@ -23655,23 +23764,23 @@ function registerHubManageTool(server, client) {
23655
23764
  }
23656
23765
 
23657
23766
  // src/tools/frame-manage.ts
23658
- import { z as z16 } from "zod";
23659
- import { mcpResult as mcpResult18, mcpError as mcpError17 } from "@walkeros/core";
23767
+ import { z as z17 } from "zod";
23768
+ import { mcpResult as mcpResult18, mcpError as mcpError19 } from "@walkeros/core";
23660
23769
  var FRAME_ID_PATTERN2 = /^frm_[A-Za-z0-9_-]{21}$/;
23661
23770
  var MAX_PAGE_KEY_CHARS = 1024;
23662
23771
  var TITLE17 = "Frames";
23663
23772
  var FRAME_MANAGE_DESCRIPTION = 'Read the frames of a measurement plan: named rectangles with marks inside them, drawn in Tag Mode or in the app. Actions: list (every frame of the project, without marks), page (the frames of one page at any depth, with marks), get (one frame with its marks). Read-only: frames are drawn and edited in Tag Mode or the app, never here. A frame name is documentation; the marks inside it carry the meaning. A frame that extends another stores only what it adds, so its tags may carry only the fields they change. Marks are { tags, note }: tags is one flat list of tags, each with an id, a kind such as entity, property or action, a name, and a parentId naming the tag it sits under; note is the frame\u2019s own description and thread. Use hub_manage action "knowledge" with a frameId or markId to read what people wrote on a frame. A markId is a tag id: a tag\u2019s id, parentId and threadRef come back literal when they have the shape the app mints, so they can be passed straight back; every other string value in the marks is wrapped as data, and so is an object key that is not a plain identifier.';
23664
23773
  var FRAME_MANAGE_INPUT_SCHEMA = {
23665
- action: z16.enum(["list", "page", "get"]).describe(
23774
+ action: z17.enum(["list", "page", "get"]).describe(
23666
23775
  "list the project\u2019s frames, read one page with marks, or read one frame"
23667
23776
  ),
23668
- projectId: z16.string().optional().describe(
23777
+ projectId: z17.string().optional().describe(
23669
23778
  "Project ID. Optional: falls back to the default project when omitted."
23670
23779
  ),
23671
- pageKey: z16.string().min(1).max(MAX_PAGE_KEY_CHARS).optional().describe(
23780
+ pageKey: z17.string().min(1).max(MAX_PAGE_KEY_CHARS).optional().describe(
23672
23781
  "The page as its frames address it (the `source.key` of a page frame, usually the page URL without query). Required for page."
23673
23782
  ),
23674
- frameId: z16.string().regex(FRAME_ID_PATTERN2).optional().describe(
23783
+ frameId: z17.string().regex(FRAME_ID_PATTERN2).optional().describe(
23675
23784
  'Frame ID (frm_...). Required for get. Use action "list" or "page" to find one.'
23676
23785
  )
23677
23786
  };
@@ -23781,17 +23890,11 @@ function serializeFrame(frame) {
23781
23890
  marks: walkMarks(frame.marks)
23782
23891
  };
23783
23892
  }
23784
- var frameInputSchema = z16.object(FRAME_MANAGE_INPUT_SCHEMA);
23893
+ var frameInputSchema = z17.object(FRAME_MANAGE_INPUT_SCHEMA);
23785
23894
  async function frameManageHandler(client, rawInput) {
23786
23895
  const parsed = frameInputSchema.safeParse(rawInput ?? {});
23787
23896
  if (!parsed.success) {
23788
- return mcpError17(
23789
- new Error(
23790
- parsed.error.issues.map(
23791
- (issue) => `${issue.path.join(".") || "input"}: ${issue.message}`
23792
- ).join("; ")
23793
- )
23794
- );
23897
+ return mcpError19(new Error(inputIssuesMessage(parsed.error)));
23795
23898
  }
23796
23899
  const { action, projectId, pageKey, frameId } = parsed.data;
23797
23900
  const validationError = validateActionInput(
@@ -23800,11 +23903,11 @@ async function frameManageHandler(client, rawInput) {
23800
23903
  { pageKey, frameId },
23801
23904
  FRAME_MANAGE_REQUIREMENTS
23802
23905
  );
23803
- if (validationError) return mcpError17(new Error(validationError));
23906
+ if (validationError) return mcpError19(new Error(validationError));
23804
23907
  try {
23805
23908
  const resolvedProjectId = resolveDefaultProject(client, projectId);
23806
23909
  if (!resolvedProjectId) {
23807
- return mcpError17(new Error(NO_DEFAULT_PROJECT_ERROR));
23910
+ return mcpError19(new Error(NO_DEFAULT_PROJECT_ERROR));
23808
23911
  }
23809
23912
  switch (action) {
23810
23913
  case "list": {
@@ -23849,7 +23952,7 @@ async function frameManageHandler(client, rawInput) {
23849
23952
  }
23850
23953
  }
23851
23954
  } catch (error) {
23852
- return mcpError17(error, errorHint(error, "frames", FRAME_NOT_FOUND_HINT));
23955
+ return mcpError19(error, errorHint(error, "frames", FRAME_NOT_FOUND_HINT));
23853
23956
  }
23854
23957
  }
23855
23958
  function createFrameManageToolSpec(client) {
@@ -23919,7 +24022,7 @@ function registerPackageSchemaResources(server) {
23919
24022
  }
23920
24023
 
23921
24024
  // src/resources/references.ts
23922
- import { schemas as schemas5 } from "@walkeros/core/dev";
24025
+ import { schemas as schemas6 } from "@walkeros/core/dev";
23923
24026
  function registerReferenceResources(server) {
23924
24027
  server.resource(
23925
24028
  "flow-schema",
@@ -23932,7 +24035,7 @@ function registerReferenceResources(server) {
23932
24035
  contents: [
23933
24036
  {
23934
24037
  uri: "walkeros://reference/flow-schema",
23935
- text: JSON.stringify(schemas5.configJsonSchema, null, 2),
24038
+ text: JSON.stringify(schemas6.configJsonSchema, null, 2),
23936
24039
  mimeType: "application/json"
23937
24040
  }
23938
24041
  ]
@@ -23949,7 +24052,7 @@ function registerReferenceResources(server) {
23949
24052
  contents: [
23950
24053
  {
23951
24054
  uri: "walkeros://reference/event-model",
23952
- text: JSON.stringify(schemas5.eventJsonSchema, null, 2),
24055
+ text: JSON.stringify(schemas6.eventJsonSchema, null, 2),
23953
24056
  mimeType: "application/json"
23954
24057
  }
23955
24058
  ]
@@ -23968,10 +24071,10 @@ function registerReferenceResources(server) {
23968
24071
  uri: "walkeros://reference/mapping",
23969
24072
  text: JSON.stringify(
23970
24073
  {
23971
- rules: schemas5.rulesJsonSchema,
23972
- valueConfig: schemas5.valueConfigJsonSchema,
23973
- rule: schemas5.ruleJsonSchema,
23974
- policy: schemas5.policyJsonSchema
24074
+ rules: schemas6.rulesJsonSchema,
24075
+ valueConfig: schemas6.valueConfigJsonSchema,
24076
+ rule: schemas6.ruleJsonSchema,
24077
+ policy: schemas6.policyJsonSchema
23975
24078
  },
23976
24079
  null,
23977
24080
  2
@@ -23992,7 +24095,7 @@ function registerReferenceResources(server) {
23992
24095
  contents: [
23993
24096
  {
23994
24097
  uri: "walkeros://reference/consent",
23995
- text: JSON.stringify(schemas5.consentJsonSchema, null, 2),
24098
+ text: JSON.stringify(schemas6.consentJsonSchema, null, 2),
23996
24099
  mimeType: "application/json"
23997
24100
  }
23998
24101
  ]
@@ -24068,7 +24171,7 @@ function registerReferenceResources(server) {
24068
24171
  contents: [
24069
24172
  {
24070
24173
  uri: "walkeros://reference/contract",
24071
- text: JSON.stringify(schemas5.contractJsonSchema, null, 2),
24174
+ text: JSON.stringify(schemas6.contractJsonSchema, null, 2),
24072
24175
  mimeType: "application/json"
24073
24176
  }
24074
24177
  ]
@@ -24146,17 +24249,17 @@ function registerReferenceResources(server) {
24146
24249
  }
24147
24250
 
24148
24251
  // src/prompts/add-step.ts
24149
- import { z as z17 } from "zod";
24252
+ import { z as z18 } from "zod";
24150
24253
  function registerAddStepPrompt(server) {
24151
24254
  server.registerPrompt(
24152
24255
  "add-step",
24153
24256
  {
24154
24257
  description: "Add a source, destination, transformer, or store step to a flow configuration. Guides through package selection, config scaffolding, and wiring.",
24155
24258
  argsSchema: {
24156
- stepType: z17.string().optional().describe(
24259
+ stepType: z18.string().optional().describe(
24157
24260
  "Type of step to add: source, destination, transformer, or store"
24158
24261
  ),
24159
- flowPath: z17.string().optional().describe("Path to the flow.json file to modify")
24262
+ flowPath: z18.string().optional().describe("Path to the flow.json file to modify")
24160
24263
  }
24161
24264
  },
24162
24265
  async ({ stepType, flowPath }) => ({
@@ -24202,14 +24305,14 @@ function registerAddStepPrompt(server) {
24202
24305
  }
24203
24306
 
24204
24307
  // src/prompts/setup-mapping.ts
24205
- import { z as z18 } from "zod";
24308
+ import { z as z19 } from "zod";
24206
24309
  function registerSetupMappingPrompt(server) {
24207
24310
  server.registerPrompt(
24208
24311
  "setup-mapping",
24209
24312
  {
24210
24313
  description: "Set up event mapping for any step in a flow. Teaches mapping syntax and uses package examples as templates.",
24211
24314
  argsSchema: {
24212
- stepName: z18.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
24315
+ stepName: z19.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
24213
24316
  }
24214
24317
  },
24215
24318
  async ({ stepName }) => ({
@@ -24247,14 +24350,14 @@ function registerSetupMappingPrompt(server) {
24247
24350
  }
24248
24351
 
24249
24352
  // src/prompts/manage-contract.ts
24250
- import { z as z19 } from "zod";
24353
+ import { z as z20 } from "zod";
24251
24354
  function registerManageContractPrompt(server) {
24252
24355
  server.registerPrompt(
24253
24356
  "manage-contract",
24254
24357
  {
24255
24358
  description: "Create or update event contracts for a flow. Can generate contracts from existing mappings or scaffold mappings from contracts.",
24256
24359
  argsSchema: {
24257
- direction: z19.string().optional().describe(
24360
+ direction: z20.string().optional().describe(
24258
24361
  'Direction: "from-mappings" (extract contract from existing mappings), "from-scratch" (create new contract), or "to-mappings" (scaffold mappings from contract)'
24259
24362
  )
24260
24363
  }
@@ -24298,7 +24401,7 @@ var SERVER_INSTRUCTIONS = `walkerOS is an open-source, privacy-first event data
24298
24401
 
24299
24402
  - **Never guess package names.** Always use \`package_search\` first to find exact names, then \`package_get\` for details.
24300
24403
  - **Never construct flow configs from memory.** Read \`walkeros://reference/flow-schema\` and use \`package_get\` for package-specific schemas.
24301
- - **Always validate.** Run \`flow_validate\` after every config change. If validation fails, fix and re-validate.
24404
+ - **Always validate.** Run \`flow_validate\` after every config change. If validation fails, fix and re-validate. By default it checks every flow and step, package settings included; a valid result can still list \`details.skipped\` checks, so read them before trusting it (\`strict: true\` turns warnings and skips into failures: \`valid\` is false).
24302
24405
  - **Simulate before deploying.** Use \`flow_simulate\` to test with mocked API calls before \`flow_bundle\` or \`flow_push\`.
24303
24406
  - **When a request fails,** call \`diagnostics\` to see which MCP/CLI versions, app URL, and backend you are on, plus whether the app is reachable.
24304
24407
 
@@ -24802,8 +24905,11 @@ var HttpToolClient = class {
24802
24905
  };
24803
24906
 
24804
24907
  // src/runtime/local.ts
24908
+ import * as fs from "fs";
24909
+ import * as path2 from "path";
24805
24910
  import {
24806
24911
  collectKnownSecrets,
24912
+ loadConfig,
24807
24913
  loadJsonConfig,
24808
24914
  bundle as bundle2,
24809
24915
  push,
@@ -24812,6 +24918,8 @@ import {
24812
24918
  simulateCollector,
24813
24919
  simulateDestination
24814
24920
  } from "@walkeros/cli";
24921
+ import { isObject as isObject3 } from "@walkeros/core";
24922
+ import { schemas as schemas7 } from "@walkeros/core/dev";
24815
24923
 
24816
24924
  // src/runtime/bundle-cache.ts
24817
24925
  import { createHash } from "crypto";
@@ -24902,6 +25010,10 @@ async function getOrBuildBundle(resolvedConfig) {
24902
25010
  function createLocalRuntime() {
24903
25011
  return {
24904
25012
  load: (input) => loadJsonConfig(input),
25013
+ baseDir(input) {
25014
+ const file = path2.resolve(input);
25015
+ return fs.existsSync(file) && fs.statSync(file).isFile() ? path2.dirname(file) : void 0;
25016
+ },
24905
25017
  bundle: (input, opts) => bundle2(input, {
24906
25018
  flowName: opts.flowName,
24907
25019
  stats: opts.stats,
@@ -24915,60 +25027,75 @@ function createLocalRuntime() {
24915
25027
  bundlePath = void 0;
24916
25028
  }
24917
25029
  const common = { bundlePath, flow: opts.flow, silent: true };
25030
+ const config = opts.config ?? input;
24918
25031
  switch (opts.stepType) {
24919
25032
  case "source":
24920
- return simulateSource(input, opts.event, {
25033
+ return simulateSource(config, opts.event, {
24921
25034
  sourceId: opts.stepId,
24922
25035
  ...common,
24923
25036
  consent: opts.state?.consent
24924
25037
  });
24925
25038
  case "transformer":
24926
- return simulateTransformer(
24927
- input,
24928
- opts.event,
24929
- {
24930
- transformerId: opts.stepId,
24931
- ...common,
24932
- ingest: opts.ingest,
24933
- consent: opts.state?.consent
24934
- }
24935
- );
25039
+ return simulateTransformer(config, partialEvent(opts.event), {
25040
+ transformerId: opts.stepId,
25041
+ ...common,
25042
+ ingest: opts.ingest,
25043
+ consent: opts.state?.consent
25044
+ });
24936
25045
  case "collector":
24937
- return simulateCollector(
24938
- input,
24939
- opts.event,
24940
- {
24941
- collectorName: opts.stepId,
24942
- ...common,
24943
- state: opts.state,
24944
- ingest: opts.ingest
24945
- }
24946
- );
25046
+ return simulateCollector(config, partialEvent(opts.event), {
25047
+ collectorName: opts.stepId,
25048
+ ...common,
25049
+ state: opts.state,
25050
+ ingest: opts.ingest
25051
+ });
24947
25052
  case "destination":
24948
- return simulateDestination(
24949
- input,
24950
- opts.event,
24951
- {
24952
- destinationId: opts.stepId,
24953
- ...common,
24954
- ingest: opts.ingest,
24955
- consent: opts.state?.consent,
24956
- command: opts.command
24957
- }
24958
- );
25053
+ return simulateDestination(config, eventObject(opts.event), {
25054
+ destinationId: opts.stepId,
25055
+ ...common,
25056
+ ingest: opts.ingest,
25057
+ consent: opts.state?.consent,
25058
+ command: opts.command
25059
+ });
24959
25060
  }
24960
25061
  },
24961
25062
  knownSecrets: async (input) => collectKnownSecrets(await loadJsonConfig(input)),
25063
+ async loadRun(input) {
25064
+ const content = await loadConfig(input, { json: false });
25065
+ const config = typeof content === "string" ? parseJson(content) : void 0;
25066
+ if (!isFlowJson(config)) return { knownSecrets: [] };
25067
+ return { config, knownSecrets: collectKnownSecrets(config) };
25068
+ },
24962
25069
  push: (input, event, opts) => push(input, event, {
24963
25070
  json: true,
24964
25071
  flow: opts.flow,
24965
- platform: opts.platform
25072
+ platform: opts.platform,
25073
+ ...opts.config !== void 0 && { raw: opts.config }
24966
25074
  })
24967
25075
  };
24968
25076
  }
25077
+ function partialEvent(value) {
25078
+ if (isPartialEvent(value)) return value;
25079
+ const parsed = schemas7.PartialEventSchema.safeParse(value);
25080
+ throw new Error(parsed.error?.message ?? "Invalid event");
25081
+ }
25082
+ function isPartialEvent(value) {
25083
+ return schemas7.PartialEventSchema.safeParse(value).success;
25084
+ }
25085
+ function eventObject(value) {
25086
+ if (isObject3(value)) return value;
25087
+ throw new Error("event must be an object.");
25088
+ }
25089
+ function parseJson(content) {
25090
+ try {
25091
+ return JSON.parse(content);
25092
+ } catch {
25093
+ return void 0;
25094
+ }
25095
+ }
24969
25096
 
24970
25097
  // src/stdio.ts
24971
- setClientContext({ type: "mcp", version: "4.7.0-next-1790722899482" });
25098
+ setClientContext({ type: "mcp", version: "4.7.0" });
24972
25099
  process.on("uncaughtException", (err) => {
24973
25100
  const emitter = getMcpEmitterSingleton();
24974
25101
  if (emitter) {
@@ -24988,7 +25115,7 @@ process.on("unhandledRejection", (reason) => {
24988
25115
  async function main() {
24989
25116
  const server = createWalkerOSMcpServer({
24990
25117
  client: new HttpToolClient(),
24991
- version: "4.7.0-next-1790722899482",
25118
+ version: "4.7.0",
24992
25119
  runtime: createLocalRuntime()
24993
25120
  });
24994
25121
  const transport = new StdioServerTransport();
@@ -24999,7 +25126,7 @@ main().catch(async (error) => {
24999
25126
  try {
25000
25127
  const emitter = await createMcpEmitter({
25001
25128
  clientInfo: void 0,
25002
- packageVersion: "4.7.0-next-1790722899482"
25129
+ packageVersion: "4.7.0"
25003
25130
  });
25004
25131
  await emitter.emitError("startup");
25005
25132
  } catch {