@walkeros/mcp 3.0.1 → 3.1.0-next-1773969156384

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/index.js CHANGED
@@ -14,7 +14,7 @@ import { z } from "zod";
14
14
  var ValidateOutputShape = {
15
15
  valid: z.boolean().describe("Whether validation passed"),
16
16
  type: z.union([
17
- z.enum(["contract", "event", "flow", "mapping"]),
17
+ z.enum(["contract", "entry", "event", "flow", "mapping"]),
18
18
  z.string().regex(/^(destinations|sources|transformers)\.\w+$/)
19
19
  ]).describe("What was validated"),
20
20
  errors: z.array(
@@ -56,15 +56,10 @@ var SimulateOutputShape = {
56
56
  z.object({
57
57
  received: z.boolean().describe("Whether destination received the event"),
58
58
  calls: z.number().describe("Number of API calls made"),
59
- payload: z.unknown().optional().describe("Transformed payload sent")
59
+ payload: z.unknown().optional().describe("Full payload (only when verbose: true)")
60
60
  })
61
61
  ).optional().describe("Per-destination results"),
62
- exampleMatch: z.object({
63
- name: z.string(),
64
- step: z.string(),
65
- match: z.boolean(),
66
- diff: z.string().optional()
67
- }).optional().describe("Example comparison result when using example parameter"),
62
+ capturedEvents: z.array(z.record(z.string(), z.unknown())).optional().describe("Events captured by source simulation"),
68
63
  duration: z.number().optional().describe("Simulation duration in ms")
69
64
  };
70
65
  var PushOutputShape = {
@@ -85,60 +80,17 @@ var ExamplesListOutputShape = {
85
80
  hasIn: z.boolean().describe("Whether the example has an input value"),
86
81
  hasOut: z.boolean().describe("Whether the example has an output value"),
87
82
  hasMapping: z.boolean().describe("Whether the example has a mapping configuration"),
83
+ hasTrigger: z.boolean().describe("Whether the example has trigger metadata"),
88
84
  in: z.unknown().optional().describe("Input event data"),
89
85
  out: z.unknown().optional().describe("Expected output data"),
90
- mapping: z.unknown().optional().describe("Mapping configuration for destinations")
86
+ mapping: z.unknown().optional().describe("Mapping configuration for destinations"),
87
+ trigger: z.object({
88
+ type: z.string().optional(),
89
+ options: z.unknown().optional()
90
+ }).optional().describe("Trigger metadata for source simulation")
91
91
  })
92
92
  ).describe("Step examples")
93
93
  };
94
- var PackageSearchOutputShape = {
95
- package: z.string().describe("Package name"),
96
- version: z.string().describe("Package version"),
97
- description: z.string().optional().describe("Package description"),
98
- type: z.string().optional().describe("Package type (destination, source, transformer)"),
99
- platform: z.string().optional().describe("Target platform (web, server)"),
100
- hintKeys: z.array(z.string()).describe("Available hint keys (use package_get section=hints to read)"),
101
- exampleSummaries: z.array(
102
- z.object({
103
- name: z.string().describe("Example name"),
104
- description: z.string().optional().describe("What this example shows")
105
- })
106
- ).describe(
107
- "Step example names and descriptions (use package_get section=examples to read full content)"
108
- )
109
- };
110
- var PackageSchemaOutputShape = {
111
- package: z.string().describe("Package name"),
112
- version: z.string().describe("Package version"),
113
- type: z.string().describe("Package type (destination, source, transformer)"),
114
- platform: z.string().describe("Target platform (web, server)"),
115
- schemas: z.record(z.string(), z.unknown()).optional().describe("JSON Schemas for settings and mapping"),
116
- examples: z.record(z.string(), z.unknown()).optional().describe(
117
- "Full configuration examples (included when section=examples or section=all)"
118
- ),
119
- exampleSummaries: z.array(
120
- z.object({
121
- name: z.string().describe("Example name"),
122
- description: z.string().optional().describe("What this example shows")
123
- })
124
- ).optional().describe(
125
- "Example names and descriptions (included in default/summary mode)"
126
- ),
127
- hints: z.record(
128
- z.string(),
129
- z.object({
130
- text: z.string(),
131
- code: z.array(
132
- z.object({
133
- lang: z.string().optional(),
134
- code: z.string()
135
- })
136
- ).optional()
137
- })
138
- ).optional().describe(
139
- "Hints \u2014 text only in summary mode, with code blocks when section=hints or section=all"
140
- )
141
- };
142
94
 
143
95
  // src/tools/validate.ts
144
96
  function registerFlowValidateTool(server2) {
@@ -270,6 +222,7 @@ function formatBytes(bytes) {
270
222
  }
271
223
 
272
224
  // src/tools/simulate.ts
225
+ import { z as z3 } from "zod";
273
226
  import { simulate } from "@walkeros/cli";
274
227
  import { schemas as schemas3 } from "@walkeros/cli/dev";
275
228
  import { mcpResult as mcpResult3, mcpError as mcpError3 } from "@walkeros/core";
@@ -278,8 +231,17 @@ function registerFlowSimulateTool(server2) {
278
231
  "flow_simulate",
279
232
  {
280
233
  title: "Simulate Flow",
281
- description: 'Simulate events through a walkerOS flow without making real API calls. Events must be in walkerOS format (post-source): { name: "entity action", data: {...} }. Raw source input (dataLayer pushes, HTTP requests) must first be converted to walkerOS events. Check source package examples to see what events a source outputs. Use the example parameter to load event input from a step example and compare output.',
282
- inputSchema: schemas3.SimulateInputShape,
234
+ 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? }, env?: {...} }. Use step to target a specific step. Use flow_examples to discover available test data.',
235
+ inputSchema: {
236
+ configPath: schemas3.SimulateInputShape.configPath,
237
+ event: z3.union([z3.record(z3.string(), z3.unknown()), z3.string()]).optional().describe(
238
+ "For destinations: { name, data }. For sources: { content, trigger?, env? }. Can also be a JSON string or file path."
239
+ ),
240
+ flow: schemas3.SimulateInputShape.flow,
241
+ platform: schemas3.SimulateInputShape.platform,
242
+ step: schemas3.SimulateInputShape.step,
243
+ verbose: z3.boolean().optional().describe("Include full payload per destination (default: false)")
244
+ },
283
245
  outputSchema: SimulateOutputShape,
284
246
  annotations: {
285
247
  readOnlyHint: true,
@@ -288,26 +250,51 @@ function registerFlowSimulateTool(server2) {
288
250
  openWorldHint: false
289
251
  }
290
252
  },
291
- async ({ configPath, event, flow, platform, example, step }) => {
253
+ async ({ configPath, event, flow, platform, step, verbose }) => {
292
254
  try {
293
- if (!event && !example) {
294
- throw new Error("Either event or example must be provided");
255
+ if (!event) {
256
+ throw new Error(
257
+ "event is required. For sources provide { content, trigger? }, for destinations provide { name, data }."
258
+ );
295
259
  }
296
260
  const raw = await simulate(configPath, event, {
297
261
  json: true,
298
262
  flow,
299
263
  platform,
300
- example,
301
264
  step
302
265
  });
266
+ if (raw.capturedEvents) {
267
+ const eventCount = raw.capturedEvents.length;
268
+ const summary2 = `Source captured ${eventCount} event${eventCount !== 1 ? "s" : ""}`;
269
+ return mcpResult3(
270
+ {
271
+ success: raw.success,
272
+ error: raw.error,
273
+ summary: summary2,
274
+ capturedEvents: raw.capturedEvents,
275
+ duration: raw.duration
276
+ },
277
+ summary2,
278
+ {
279
+ next: eventCount > 0 ? [
280
+ "Use flow_simulate with a destination step to test downstream processing"
281
+ ] : [
282
+ "Check source package examples with package_get, verify trigger type matches"
283
+ ]
284
+ }
285
+ );
286
+ }
303
287
  const destinations = {};
304
288
  if (raw.usage) {
305
289
  for (const [name, calls] of Object.entries(raw.usage)) {
306
- destinations[name] = {
290
+ const summary2 = {
307
291
  received: calls.length > 0,
308
- calls: calls.length,
309
- payload: calls.length > 0 ? calls[calls.length - 1] : void 0
292
+ calls: calls.length
310
293
  };
294
+ if (verbose && calls.length > 0) {
295
+ summary2.payload = calls[calls.length - 1];
296
+ }
297
+ destinations[name] = summary2;
311
298
  }
312
299
  }
313
300
  const destCount = Object.keys(destinations).length;
@@ -316,13 +303,11 @@ function registerFlowSimulateTool(server2) {
316
303
  ).length;
317
304
  const warnings = [];
318
305
  if (destCount === 0) {
319
- warnings.push(
320
- "No destinations found in flow configuration. Check that your flow defines at least one destination."
321
- );
306
+ warnings.push("No destinations found in flow configuration.");
322
307
  }
323
308
  if (destCount > 0 && receivedCount === 0) {
324
309
  warnings.push(
325
- 'No destinations received the event. Most common cause: mapping keys must be NESTED entity \u2192 action objects \u2014 event "product add" needs { "product": { "add": Rule } }, not "product.add". Also check event name match and consent settings.'
310
+ "No destinations received the event. Check: mapping keys use nested entity\u2192action structure, event name matches, consent is granted."
326
311
  );
327
312
  }
328
313
  const summary = `${receivedCount}/${destCount} destinations received the event`;
@@ -331,7 +316,6 @@ function registerFlowSimulateTool(server2) {
331
316
  error: raw.error,
332
317
  summary,
333
318
  destinations: destCount > 0 ? destinations : void 0,
334
- exampleMatch: raw.exampleMatch,
335
319
  duration: raw.duration
336
320
  };
337
321
  return mcpResult3(result, summary, {
@@ -346,6 +330,7 @@ function registerFlowSimulateTool(server2) {
346
330
  }
347
331
 
348
332
  // src/tools/push.ts
333
+ import { z as z4 } from "zod";
349
334
  import { push } from "@walkeros/cli";
350
335
  import { schemas as schemas4 } from "@walkeros/cli/dev";
351
336
  import { mcpResult as mcpResult4, mcpError as mcpError4 } from "@walkeros/core";
@@ -354,8 +339,15 @@ function registerFlowPushTool(server2) {
354
339
  "flow_push",
355
340
  {
356
341
  title: "Push Events",
357
- description: "Push a real event through a walkerOS flow to actual destinations. WARNING: This makes real API calls to real endpoints. Note: Web destinations (gtag, meta, etc.) require browser globals that are not available in Node.js. For web flows, use flow_simulate to test. flow_push works best for server-side flows.",
358
- inputSchema: schemas4.PushInputShape,
342
+ description: "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.",
343
+ inputSchema: {
344
+ configPath: schemas4.PushInputShape.configPath,
345
+ event: z4.record(z4.string(), z4.unknown()).describe(
346
+ 'Event object, e.g. { name: "page view", data: { title: "Home" } }'
347
+ ),
348
+ flow: schemas4.PushInputShape.flow,
349
+ platform: schemas4.PushInputShape.platform
350
+ },
359
351
  outputSchema: PushOutputShape,
360
352
  annotations: {
361
353
  readOnlyHint: false,
@@ -374,7 +366,7 @@ function registerFlowPushTool(server2) {
374
366
  if (!result.success) {
375
367
  return mcpError4(
376
368
  new Error(result.error || "Push failed"),
377
- "Check destination configuration and network connectivity. For web destinations, use flow_simulate instead."
369
+ "Check destination configuration and connectivity."
378
370
  );
379
371
  }
380
372
  const summary = `Pushed event${result.duration ? ` (${result.duration}ms)` : ""}`;
@@ -382,7 +374,7 @@ function registerFlowPushTool(server2) {
382
374
  } catch (error) {
383
375
  return mcpError4(
384
376
  error,
385
- "Check configPath and event format. For web destinations, use flow_simulate instead."
377
+ "Check configPath and event format. For web flows, use flow_simulate."
386
378
  );
387
379
  }
388
380
  }
@@ -390,7 +382,7 @@ function registerFlowPushTool(server2) {
390
382
  }
391
383
 
392
384
  // src/tools/examples.ts
393
- import { z as z3 } from "zod";
385
+ import { z as z5 } from "zod";
394
386
  import { loadJsonConfig } from "@walkeros/cli";
395
387
  import { mcpResult as mcpResult5, mcpError as mcpError5 } from "@walkeros/core";
396
388
  function registerFlowExamplesTool(server2) {
@@ -400,10 +392,12 @@ function registerFlowExamplesTool(server2) {
400
392
  title: "Flow Examples",
401
393
  description: "List all step examples in a walkerOS flow configuration. Shows example names, step locations, and in/out shapes. Use this to discover available test fixtures and simulation data.",
402
394
  inputSchema: {
403
- configPath: z3.string().min(1).describe("Path to flow configuration file"),
404
- flow: z3.string().optional().describe("Flow name for multi-flow configs"),
405
- step: z3.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
406
- full: z3.boolean().optional().describe(
395
+ configPath: z5.string().min(1).describe(
396
+ "Path to flow configuration file, URL, or inline JSON string"
397
+ ),
398
+ flow: z5.string().optional().describe("Flow name for multi-flow configs"),
399
+ step: z5.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
400
+ full: z5.boolean().optional().describe(
407
401
  "Return full in/out/mapping data for each example (default: false, returns metadata only)"
408
402
  )
409
403
  },
@@ -451,7 +445,13 @@ function registerFlowExamplesTool(server2) {
451
445
  hasIn: ex.in !== void 0,
452
446
  hasOut: ex.out !== void 0,
453
447
  hasMapping: ex.mapping !== void 0,
454
- ...full ? { in: ex.in, out: ex.out, mapping: ex.mapping } : {}
448
+ hasTrigger: ex.trigger !== void 0,
449
+ ...full ? {
450
+ in: ex.in,
451
+ out: ex.out,
452
+ mapping: ex.mapping,
453
+ trigger: ex.trigger
454
+ } : {}
455
455
  });
456
456
  }
457
457
  }
@@ -465,7 +465,7 @@ function registerFlowExamplesTool(server2) {
465
465
  const totalExamples = examples.length;
466
466
  const summary = `${totalExamples} examples across ${stepSet.size} steps`;
467
467
  const hints = {
468
- next: ["Use flow_simulate with example parameter to test"]
468
+ next: ["Use flow_simulate with step and event to simulate"]
469
469
  };
470
470
  if (totalExamples === 0) {
471
471
  hints.warnings = [
@@ -481,207 +481,85 @@ function registerFlowExamplesTool(server2) {
481
481
  }
482
482
 
483
483
  // src/tools/package.ts
484
- import { z as z4 } from "zod";
485
- import { fetchPackage, mcpResult as mcpResult6, mcpError as mcpError6 } from "@walkeros/core";
484
+ import { z as z6 } from "zod";
485
+ import {
486
+ fetchPackage,
487
+ mergeConfigSchema,
488
+ mcpResult as mcpResult6,
489
+ mcpError as mcpError6
490
+ } from "@walkeros/core";
486
491
 
487
- // src/registry.ts
488
- var PACKAGE_REGISTRY = [
489
- // Web Destinations
490
- {
491
- name: "@walkeros/web-destination-gtag",
492
- type: "destination",
493
- platform: "web",
494
- description: "Google destination (GA4, Ads, GTM via gtag.js)"
495
- },
496
- {
497
- name: "@walkeros/web-destination-meta",
498
- type: "destination",
499
- platform: "web",
500
- description: "Meta (Facebook) Pixel"
501
- },
502
- {
503
- name: "@walkeros/web-destination-plausible",
504
- type: "destination",
505
- platform: "web",
506
- description: "Plausible Analytics"
507
- },
508
- {
509
- name: "@walkeros/web-destination-snowplow",
510
- type: "destination",
511
- platform: "web",
512
- description: "Snowplow Analytics"
513
- },
514
- {
515
- name: "@walkeros/web-destination-piwikpro",
516
- type: "destination",
517
- platform: "web",
518
- description: "Piwik PRO Analytics"
519
- },
520
- {
521
- name: "@walkeros/web-destination-api",
522
- type: "destination",
523
- platform: "web",
524
- description: "Generic HTTP API destination"
525
- },
526
- // Server Destinations
527
- {
528
- name: "@walkeros/server-destination-gcp",
529
- type: "destination",
530
- platform: "server",
531
- description: "Google Cloud Platform (BigQuery)"
532
- },
533
- {
534
- name: "@walkeros/server-destination-aws",
535
- type: "destination",
536
- platform: "server",
537
- description: "AWS (Firehose)"
538
- },
539
- {
540
- name: "@walkeros/server-destination-meta",
541
- type: "destination",
542
- platform: "server",
543
- description: "Meta Conversions API (server-side)"
544
- },
545
- {
546
- name: "@walkeros/server-destination-api",
547
- type: "destination",
548
- platform: "server",
549
- description: "Generic HTTP API destination (server)"
550
- },
551
- {
552
- name: "@walkeros/server-destination-datamanager",
553
- type: "destination",
554
- platform: "server",
555
- description: "Google Data Manager"
556
- },
557
- // Web Sources
558
- {
559
- name: "@walkeros/web-source-browser",
560
- type: "source",
561
- platform: "web",
562
- description: "Browser DOM event capture (clicks, page views, forms)"
563
- },
564
- {
565
- name: "@walkeros/web-source-datalayer",
566
- type: "source",
567
- platform: "web",
568
- description: "Google Tag Manager dataLayer bridge"
569
- },
570
- {
571
- name: "@walkeros/web-source-session",
572
- type: "source",
573
- platform: "web",
574
- description: "Session tracking source"
575
- },
576
- // CMP Sources
577
- {
578
- name: "@walkeros/web-source-cmp-cookiefirst",
579
- type: "source",
580
- platform: "web",
581
- description: "CookieFirst consent management"
582
- },
583
- {
584
- name: "@walkeros/web-source-cmp-cookiepro",
585
- type: "source",
586
- platform: "web",
587
- description: "CookiePro/OneTrust consent management"
588
- },
589
- {
590
- name: "@walkeros/web-source-cmp-usercentrics",
591
- type: "source",
592
- platform: "web",
593
- description: "Usercentrics consent management"
594
- },
595
- // Server Sources
596
- {
597
- name: "@walkeros/server-source-express",
598
- type: "source",
599
- platform: "server",
600
- description: "Express.js HTTP event endpoint"
601
- },
602
- {
603
- name: "@walkeros/server-source-fetch",
604
- type: "source",
605
- platform: "server",
606
- description: "Web Fetch API source (Cloudflare, Vercel Edge, Deno, Bun)"
607
- },
608
- {
609
- name: "@walkeros/server-source-aws",
610
- type: "source",
611
- platform: "server",
612
- description: "AWS sources (Lambda, API Gateway, Function URLs)"
613
- },
614
- {
615
- name: "@walkeros/server-source-gcp",
616
- type: "source",
617
- platform: "server",
618
- description: "GCP sources (Cloud Functions)"
619
- },
620
- // Transformers
621
- {
622
- name: "@walkeros/transformer-router",
623
- type: "transformer",
624
- platform: "universal",
625
- description: "Route events to different destination subsets"
626
- },
627
- {
628
- name: "@walkeros/transformer-validator",
629
- type: "transformer",
630
- platform: "universal",
631
- description: "Event validation using JSON Schema"
632
- },
633
- {
634
- name: "@walkeros/server-transformer-fingerprint",
635
- type: "transformer",
636
- platform: "server",
637
- description: "Device fingerprinting for anonymous user identification"
638
- },
639
- {
640
- name: "@walkeros/server-transformer-cache",
641
- type: "transformer",
642
- platform: "server",
643
- description: "HTTP response caching with LRU eviction"
644
- },
645
- {
646
- name: "@walkeros/server-transformer-file",
647
- type: "transformer",
648
- platform: "server",
649
- description: "File serving transformer for static files"
650
- },
651
- // Stores
652
- {
653
- name: "@walkeros/store-memory",
654
- type: "store",
655
- platform: "universal",
656
- description: "In-memory key-value store with LRU eviction and TTL"
657
- },
658
- {
659
- name: "@walkeros/server-store-fs",
660
- type: "store",
661
- platform: "server",
662
- description: "File system key-value store"
663
- },
664
- {
665
- name: "@walkeros/server-store-s3",
666
- type: "store",
667
- platform: "server",
668
- description: "AWS S3 key-value store"
669
- },
670
- {
671
- name: "@walkeros/server-store-gcs",
672
- type: "store",
673
- platform: "server",
674
- description: "Google Cloud Storage key-value store"
492
+ // src/catalog.ts
493
+ var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
494
+ var JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm";
495
+ var WALKEROS_JSON_PATH = "dist/walkerOS.json";
496
+ var CACHE_TTL = 5 * 60 * 1e3;
497
+ var cache;
498
+ function normalizePlatform(platform) {
499
+ if (platform == null) return [];
500
+ if (typeof platform === "string") {
501
+ return platform === "universal" ? ["web", "server"] : [platform];
675
502
  }
676
- ];
677
- function filterRegistry(filters) {
678
- let results = PACKAGE_REGISTRY;
503
+ if (Array.isArray(platform)) {
504
+ return platform.filter((v) => typeof v === "string");
505
+ }
506
+ return [];
507
+ }
508
+ async function fetchCatalog(filters) {
509
+ if (cache && Date.now() - cache.timestamp < CACHE_TTL) {
510
+ return applyFilters(cache.entries, filters);
511
+ }
512
+ let entries;
513
+ try {
514
+ entries = await fetchFromNpm();
515
+ } catch {
516
+ return [];
517
+ }
518
+ cache = { entries, timestamp: Date.now() };
519
+ return applyFilters(entries, filters);
520
+ }
521
+ async function fetchFromNpm() {
522
+ const res = await fetch(`${NPM_SEARCH_URL}?text=@walkeros/&size=250`, {
523
+ signal: AbortSignal.timeout(1e4)
524
+ });
525
+ if (!res.ok) throw new Error(`npm search failed: ${res.status}`);
526
+ const data = await res.json();
527
+ const metaResults = await Promise.allSettled(
528
+ data.objects.map((obj) => enrichWithMeta(obj.package))
529
+ );
530
+ return metaResults.filter(
531
+ (r) => r.status === "fulfilled"
532
+ ).map((r) => r.value).filter((entry) => entry !== void 0);
533
+ }
534
+ async function enrichWithMeta(pkg) {
535
+ try {
536
+ const res = await fetch(
537
+ `${JSDELIVR_BASE}/${pkg.name}@${pkg.version}/${WALKEROS_JSON_PATH}`,
538
+ { signal: AbortSignal.timeout(5e3) }
539
+ );
540
+ if (!res.ok) return void 0;
541
+ const json = await res.json();
542
+ const meta = json.$meta;
543
+ if (!meta || typeof meta.type !== "string") return void 0;
544
+ return {
545
+ name: pkg.name,
546
+ version: pkg.version,
547
+ description: pkg.description,
548
+ type: meta.type,
549
+ platform: normalizePlatform(meta.platform)
550
+ };
551
+ } catch {
552
+ return void 0;
553
+ }
554
+ }
555
+ function applyFilters(entries, filters) {
556
+ let results = entries;
679
557
  if (filters?.type) {
680
- results = results.filter((p) => p.type === filters.type);
558
+ results = results.filter((e) => e.type === filters.type);
681
559
  }
682
560
  if (filters?.platform) {
683
561
  results = results.filter(
684
- (p) => p.platform === filters.platform || p.platform === "universal"
562
+ (e) => e.platform.length === 0 || e.platform.includes(filters.platform)
685
563
  );
686
564
  }
687
565
  return results;
@@ -695,14 +573,14 @@ function registerPackageSearchTool(server2) {
695
573
  title: "Search Package",
696
574
  description: "Browse walkerOS packages or look up a specific one. Without package name: returns catalog filtered by type/platform. With package name: returns metadata, hint keys, and example summaries.",
697
575
  inputSchema: {
698
- package: z4.string().min(1).optional().describe(
576
+ package: z6.string().min(1).optional().describe(
699
577
  "Exact npm package name for detailed lookup (e.g., @walkeros/web-destination-snowplow)"
700
578
  ),
701
- type: z4.enum(["source", "destination", "transformer", "store"]).optional().describe("Filter by package type (browse mode)"),
702
- platform: z4.enum(["web", "server"]).optional().describe(
579
+ type: z6.enum(["source", "destination", "transformer", "store"]).optional().describe("Filter by package type (browse mode)"),
580
+ platform: z6.enum(["web", "server"]).optional().describe(
703
581
  "Filter by platform (browse mode, includes universal packages)"
704
582
  ),
705
- version: z4.string().optional().describe("Package version for detailed lookup (default: latest)")
583
+ version: z6.string().optional().describe("Package version for detailed lookup (default: latest)")
706
584
  },
707
585
  // No outputSchema: browse mode returns {catalog, count}, lookup returns metadata — incompatible shapes
708
586
  annotations: {
@@ -714,7 +592,7 @@ function registerPackageSearchTool(server2) {
714
592
  },
715
593
  async ({ package: packageName, type, platform, version }) => {
716
594
  if (!packageName) {
717
- const catalog = filterRegistry({ type, platform });
595
+ const catalog = await fetchCatalog({ type, platform });
718
596
  const result = { catalog, count: catalog.length };
719
597
  const summary = `${catalog.length} packages found`;
720
598
  return mcpResult6(result, summary, {
@@ -728,7 +606,7 @@ function registerPackageSearchTool(server2) {
728
606
  version: info.version,
729
607
  description: info.description,
730
608
  type: info.type,
731
- platform: info.platform,
609
+ platform: normalizePlatform(info.platform),
732
610
  hintKeys: info.hintKeys,
733
611
  exampleSummaries: info.exampleSummaries
734
612
  };
@@ -752,15 +630,15 @@ function registerGetPackageSchemaTool(server2) {
752
630
  title: "Get Package",
753
631
  description: 'Fetch walkerOS package details from npm. By default returns schemas + hint texts + example summaries (lightweight). Use section parameter to get full content: "hints" (with code blocks), "examples" (full in/out data), or "all" (everything). Use package_search first to browse available packages.',
754
632
  inputSchema: {
755
- package: z4.string().min(1).describe(
633
+ package: z6.string().min(1).describe(
756
634
  "Exact npm package name (e.g., @walkeros/web-destination-snowplow)"
757
635
  ),
758
- version: z4.string().optional().describe("Package version (default: latest)"),
759
- section: z4.enum(["hints", "examples", "all"]).optional().describe(
636
+ version: z6.string().optional().describe("Package version (default: latest)"),
637
+ section: z6.enum(["hints", "examples", "all"]).optional().describe(
760
638
  "Section to expand with full content. Default: summary view with schemas + hint texts + example descriptions"
761
639
  )
762
640
  },
763
- outputSchema: PackageSchemaOutputShape,
641
+ // No outputSchema — removed to avoid SDK -32602 crashes on unexpected field values
764
642
  annotations: {
765
643
  readOnlyHint: true,
766
644
  destructiveHint: false,
@@ -771,12 +649,24 @@ function registerGetPackageSchemaTool(server2) {
771
649
  async ({ package: packageName, version, section }) => {
772
650
  try {
773
651
  const info = await fetchPackage(packageName, { version });
652
+ const mergedSchemas = {};
653
+ if (info.type) {
654
+ mergedSchemas.config = mergeConfigSchema(
655
+ info.type,
656
+ info.schemas
657
+ );
658
+ }
659
+ for (const [key, value] of Object.entries(info.schemas)) {
660
+ if (key !== "settings") {
661
+ mergedSchemas[key] = value;
662
+ }
663
+ }
774
664
  const result = {
775
665
  package: info.packageName,
776
666
  version: info.version,
777
667
  type: info.type,
778
- platform: info.platform,
779
- schemas: info.schemas
668
+ platform: normalizePlatform(info.platform),
669
+ schemas: mergedSchemas
780
670
  };
781
671
  if (info.hints) {
782
672
  if (section === "hints" || section === "all") {
@@ -810,7 +700,7 @@ function registerGetPackageSchemaTool(server2) {
810
700
  }
811
701
 
812
702
  // src/tools/flow-load.ts
813
- import { z as z5 } from "zod";
703
+ import { z as z7 } from "zod";
814
704
  import { loadJsonConfig as loadJsonConfig2 } from "@walkeros/cli";
815
705
  import { mcpResult as mcpResult7, mcpError as mcpError7 } from "@walkeros/core";
816
706
  var WEB_SKELETON = {
@@ -842,16 +732,16 @@ function registerFlowLoadTool(server2) {
842
732
  title: "Load or Create Flow",
843
733
  description: "Load an existing flow configuration from a local file path, URL, or walkerOS API (by flow ID). 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.",
844
734
  inputSchema: {
845
- source: z5.string().optional().describe(
846
- "Flow source: local file path (./flow.json), URL (https://...), or API flow ID (cfg_...). Omit to create a new flow."
735
+ source: z7.string().optional().describe(
736
+ "Flow source: local file path (./flow.json), URL (https://...), inline JSON string, or API flow ID (cfg_...). Omit to create a new flow."
847
737
  ),
848
- platform: z5.enum(["web", "server"]).optional().describe(
738
+ platform: z7.enum(["web", "server"]).optional().describe(
849
739
  "Platform for new flows. Required when source is omitted. web = browser tracking, server = Node.js HTTP."
850
740
  )
851
741
  },
852
742
  outputSchema: {
853
- version: z5.number().describe("Flow config version"),
854
- flows: z5.record(z5.string(), z5.unknown()).describe("Flow definitions")
743
+ version: z7.number().describe("Flow config version"),
744
+ flows: z7.record(z7.string(), z7.unknown()).describe("Flow definitions")
855
745
  },
856
746
  annotations: {
857
747
  readOnlyHint: true,
@@ -906,8 +796,63 @@ function registerFlowLoadTool(server2) {
906
796
  );
907
797
  }
908
798
 
799
+ // src/tools/feedback.ts
800
+ import { z as z8 } from "zod";
801
+ import { feedback, readConfig, writeConfig } from "@walkeros/cli";
802
+ import { mcpResult as mcpResult8, mcpError as mcpError8 } from "@walkeros/core";
803
+ function registerFeedbackTool(server2) {
804
+ server2.registerTool(
805
+ "feedback",
806
+ {
807
+ title: "Send Feedback",
808
+ description: "Send feedback about walkerOS",
809
+ inputSchema: {
810
+ text: z8.string().describe("Your feedback text"),
811
+ anonymous: z8.boolean().optional().describe(
812
+ "Include user/project info? false = include, true = anonymous. Only needed on first call if not yet configured."
813
+ )
814
+ },
815
+ annotations: {
816
+ readOnlyHint: false,
817
+ destructiveHint: false,
818
+ idempotentHint: false,
819
+ openWorldHint: true
820
+ }
821
+ },
822
+ async (params) => {
823
+ try {
824
+ const { text, anonymous: explicitAnonymous } = params;
825
+ const config = readConfig();
826
+ let anonymous = config?.anonymousFeedback;
827
+ if (anonymous === void 0 && explicitAnonymous === void 0) {
828
+ return mcpResult8(
829
+ { needsConsent: true },
830
+ 'Before sending feedback, ask the user: "Would you like to include your user and project info with feedback? This is a one-time choice." Then call feedback again with the anonymous parameter set.',
831
+ {
832
+ next: [
833
+ "Ask the user if they want to include their info",
834
+ "Call feedback again with anonymous: true or false"
835
+ ]
836
+ }
837
+ );
838
+ }
839
+ if (anonymous === void 0 && explicitAnonymous !== void 0) {
840
+ anonymous = explicitAnonymous;
841
+ const base = config ?? { token: "", email: "", appUrl: "" };
842
+ writeConfig({ ...base, anonymousFeedback: anonymous });
843
+ }
844
+ const isAnonymous = explicitAnonymous ?? anonymous ?? true;
845
+ await feedback(text, { anonymous: isAnonymous, version: "3.1.0-next-1773969156384" });
846
+ return mcpResult8({ ok: true }, "Feedback sent. Thanks!");
847
+ } catch (error) {
848
+ return mcpError8(error);
849
+ }
850
+ }
851
+ );
852
+ }
853
+
909
854
  // src/tools/api.ts
910
- import { z as z7 } from "zod";
855
+ import { z as z10 } from "zod";
911
856
  import {
912
857
  whoami,
913
858
  listProjects,
@@ -928,14 +873,14 @@ import {
928
873
  createDeployment as createDep,
929
874
  deleteDeployment as deleteDep
930
875
  } from "@walkeros/cli";
931
- import { mcpResult as mcpResult8, mcpError as mcpError8 } from "@walkeros/core";
876
+ import { mcpResult as mcpResult9, mcpError as mcpError9 } from "@walkeros/core";
932
877
 
933
878
  // src/schemas/api-output.ts
934
- import { z as z6 } from "zod";
879
+ import { z as z9 } from "zod";
935
880
  var ApiOutputShape = {
936
- action: z6.string().describe("Action that was executed"),
937
- ok: z6.boolean().describe("Whether the action succeeded"),
938
- data: z6.unknown().describe("Action-specific result data")
881
+ action: z9.string().describe("Action that was executed"),
882
+ ok: z9.boolean().describe("Whether the action succeeded"),
883
+ data: z9.unknown().describe("Action-specific result data")
939
884
  };
940
885
 
941
886
  // src/tools/api.ts
@@ -963,21 +908,26 @@ function registerApiTool(server2) {
963
908
  "api",
964
909
  {
965
910
  title: "walkerOS Cloud API",
966
- description: "Manage walkerOS cloud projects, flows, and deployments. Requires WALKEROS_TOKEN env var.\n\nActions:\n- whoami \u2014 verify token, get user info\n- project.list/get/create/update/delete \u2014 manage projects\n- flow.list/get/create/update/delete/duplicate \u2014 manage flow configs\n- deploy \u2014 deploy a flow (auto-detects web/server)\n- deployment.get/list/create/delete \u2014 manage deployments\n\nParameters vary by action. id = flowId/projectId/slug depending on context. content = Flow.Config JSON for flow.create/update.",
911
+ description: "Manage walkerOS cloud projects, flows, and deployments. Requires WALKEROS_TOKEN env var.\n\nActions:\n- whoami \u2014 verify token, get user info\n- project.list/get/create/update/delete \u2014 manage projects\n- flow.list/get/create/update/delete/duplicate \u2014 manage flow configs\n- deploy \u2014 deploy a flow (auto-detects web/server)\n- deployment.get/list/create/delete \u2014 manage deployments\n\nParameters vary by action. content = Flow.Config JSON for flow.create/update.",
967
912
  inputSchema: {
968
- action: z7.enum(ACTIONS).describe("API action to perform"),
969
- id: z7.string().optional().describe("Resource ID (flowId, projectId, or deployment slug)"),
970
- name: z7.string().optional().describe("Name for create/update operations"),
971
- content: z7.record(z7.string(), z7.unknown()).optional().describe("Flow.Config JSON for flow operations"),
972
- patch: z7.boolean().optional().describe("Use merge-patch for flow.update (default: true)"),
973
- wait: z7.boolean().optional().describe("Wait for deploy to complete (default: true)"),
974
- flowName: z7.string().optional().describe("Flow name for multi-settings flows"),
975
- fields: z7.array(z7.string()).optional().describe("Dot-path field selectors for flow.get"),
976
- type: z7.enum(["web", "server"]).optional().describe("Deployment type for deployment.create"),
977
- sort: z7.string().optional().describe("Sort field for list operations"),
978
- order: z7.enum(["asc", "desc"]).optional().describe("Sort order"),
979
- status: z7.string().optional().describe("Status filter for deployment.list"),
980
- includeDeleted: z7.boolean().optional().describe("Include deleted items in lists")
913
+ action: z10.enum(ACTIONS).describe("API action to perform"),
914
+ projectId: z10.string().optional().describe(
915
+ "Project ID (proj_...). Required for: project.get/update/delete, flow.create, flow.list. Falls back to WALKEROS_PROJECT_ID env var."
916
+ ),
917
+ flowId: z10.string().optional().describe(
918
+ "Flow ID (flow_...) or config ID (cfg_...). Required for: flow.get, flow.update, flow.delete, flow.duplicate, deploy. For deployment.get/delete: can be a deployment slug."
919
+ ),
920
+ name: z10.string().optional().describe("Name for create/update operations"),
921
+ content: z10.record(z10.string(), z10.unknown()).optional().describe("Flow.Config JSON for flow operations"),
922
+ patch: z10.boolean().optional().describe("Use merge-patch for flow.update (default: true)"),
923
+ wait: z10.boolean().optional().describe("Wait for deploy to complete (default: true)"),
924
+ flowName: z10.string().optional().describe("Flow name for multi-settings flows"),
925
+ fields: z10.array(z10.string()).optional().describe("Dot-path field selectors for flow.get"),
926
+ type: z10.enum(["web", "server"]).optional().describe("Deployment type for deployment.create"),
927
+ sort: z10.string().optional().describe("Sort field for list operations"),
928
+ order: z10.enum(["asc", "desc"]).optional().describe("Sort order"),
929
+ status: z10.string().optional().describe("Status filter for deployment.list"),
930
+ includeDeleted: z10.boolean().optional().describe("Include deleted items in lists")
981
931
  },
982
932
  outputSchema: ApiOutputShape,
983
933
  annotations: {
@@ -990,7 +940,8 @@ function registerApiTool(server2) {
990
940
  async (params, extra) => {
991
941
  const {
992
942
  action,
993
- id,
943
+ projectId,
944
+ flowId,
994
945
  name,
995
946
  content,
996
947
  patch,
@@ -1020,7 +971,7 @@ function registerApiTool(server2) {
1020
971
  break;
1021
972
  }
1022
973
  case "project.get": {
1023
- data = await getProject({ projectId: id });
974
+ data = await getProject({ projectId });
1024
975
  summary = `Project "${data.name}"`;
1025
976
  break;
1026
977
  }
@@ -1032,19 +983,19 @@ function registerApiTool(server2) {
1032
983
  }
1033
984
  case "project.update": {
1034
985
  if (!name) throw new Error("name required for project.update");
1035
- data = await updateProject({ projectId: id, name });
986
+ data = await updateProject({ projectId, name });
1036
987
  summary = `Updated project "${name}"`;
1037
988
  break;
1038
989
  }
1039
990
  case "project.delete": {
1040
- data = await deleteProject({ projectId: id });
1041
- summary = `Deleted project ${id ?? "default"}`;
991
+ data = await deleteProject({ projectId });
992
+ summary = `Deleted project ${projectId ?? "default"}`;
1042
993
  break;
1043
994
  }
1044
995
  // Flows
1045
996
  case "flow.list": {
1046
997
  data = await listFlows({
1047
- projectId: id,
998
+ projectId,
1048
999
  sort,
1049
1000
  order,
1050
1001
  includeDeleted
@@ -1053,79 +1004,106 @@ function registerApiTool(server2) {
1053
1004
  break;
1054
1005
  }
1055
1006
  case "flow.get": {
1056
- if (!id) throw new Error("id required for flow.get");
1057
- data = await getFlow({ flowId: id, fields });
1058
- summary = `Flow "${data.name}" (${id})`;
1007
+ if (!flowId) throw new Error("flowId required for flow.get");
1008
+ data = await getFlow({ flowId, projectId, fields });
1009
+ summary = `Flow "${data.name}" (${flowId})`;
1059
1010
  break;
1060
1011
  }
1061
1012
  case "flow.create": {
1062
1013
  if (!name) throw new Error("name required for flow.create");
1063
1014
  if (!content) throw new Error("content required for flow.create");
1064
- data = await createFlow({ name, content });
1015
+ data = await createFlow({ name, content, projectId });
1065
1016
  summary = `Created flow "${name}" (${data.id})`;
1066
1017
  break;
1067
1018
  }
1068
1019
  case "flow.update": {
1069
- if (!id) throw new Error("id required for flow.update");
1020
+ if (!flowId) throw new Error("flowId required for flow.update");
1070
1021
  data = await updateFlow({
1071
- flowId: id,
1022
+ flowId,
1072
1023
  name,
1073
1024
  content,
1025
+ projectId,
1074
1026
  mergePatch: patch ?? true
1075
1027
  });
1076
- summary = `Updated flow ${id}`;
1028
+ summary = `Updated flow ${flowId}`;
1077
1029
  break;
1078
1030
  }
1079
1031
  case "flow.delete": {
1080
- if (!id) throw new Error("id required for flow.delete");
1081
- data = await deleteFlow({ flowId: id });
1082
- summary = `Deleted flow ${id}`;
1032
+ if (!flowId) throw new Error("flowId required for flow.delete");
1033
+ data = await deleteFlow({ flowId, projectId });
1034
+ summary = `Deleted flow ${flowId}`;
1083
1035
  break;
1084
1036
  }
1085
1037
  case "flow.duplicate": {
1086
- if (!id) throw new Error("id required for flow.duplicate");
1087
- data = await duplicateFlow({ flowId: id, name });
1088
- summary = `Duplicated flow ${id}`;
1038
+ if (!flowId) throw new Error("flowId required for flow.duplicate");
1039
+ data = await duplicateFlow({ flowId, name, projectId });
1040
+ summary = `Duplicated flow ${flowId}`;
1089
1041
  break;
1090
1042
  }
1091
1043
  // Deploy
1092
1044
  case "deploy": {
1093
- if (!id) throw new Error("id (flowId) required for deploy");
1045
+ if (!flowId) throw new Error("flowId required for deploy");
1094
1046
  const progressToken = extra._meta?.progressToken;
1047
+ const DEPLOY_TIMEOUT_MS = 9e4;
1048
+ const timeoutSignal = AbortSignal.timeout(DEPLOY_TIMEOUT_MS);
1049
+ const combinedAbort = new AbortController();
1050
+ const onAbort = () => combinedAbort.abort();
1051
+ timeoutSignal.addEventListener("abort", onAbort);
1052
+ extra.signal?.addEventListener("abort", onAbort);
1095
1053
  data = await deploy({
1096
- flowId: id,
1054
+ flowId,
1055
+ projectId,
1097
1056
  wait: wait ?? true,
1098
1057
  flowName,
1058
+ timeout: DEPLOY_TIMEOUT_MS,
1099
1059
  onStatus: (s, sub) => {
1100
1060
  if (!progressToken) return;
1061
+ const key = sub ? `${s}:${sub}` : s;
1101
1062
  const stages = {
1102
- bundling: 15,
1103
- deploying: 55,
1104
- published: 100,
1105
- active: 100,
1106
- failed: 100
1063
+ "bundling:building": {
1064
+ progress: 20,
1065
+ label: "Building bundle..."
1066
+ },
1067
+ "deploying:publishing": {
1068
+ progress: 60,
1069
+ label: "Publishing to CDN..."
1070
+ },
1071
+ "deploying:provisioning": {
1072
+ progress: 60,
1073
+ label: "Provisioning container..."
1074
+ },
1075
+ "deploying:starting": {
1076
+ progress: 80,
1077
+ label: "Starting container..."
1078
+ },
1079
+ published: { progress: 100, label: "Published" },
1080
+ active: { progress: 100, label: "Active" },
1081
+ failed: { progress: 100, label: "Failed" }
1107
1082
  };
1083
+ const stage = stages[key] ?? stages[s] ?? { progress: 10, label: key };
1108
1084
  extra.sendNotification({
1109
1085
  method: "notifications/progress",
1110
1086
  params: {
1111
1087
  progressToken,
1112
- progress: stages[s] ?? 0,
1088
+ progress: stage.progress,
1113
1089
  total: 100,
1114
- message: sub ? `${s}:${sub}` : s
1090
+ message: stage.label
1115
1091
  }
1116
1092
  });
1117
1093
  },
1118
- signal: extra.signal
1094
+ signal: combinedAbort.signal
1119
1095
  });
1096
+ timeoutSignal.removeEventListener("abort", onAbort);
1097
+ extra.signal?.removeEventListener("abort", onAbort);
1120
1098
  const st = data.status;
1121
1099
  const deployData = data;
1122
1100
  if (st === "failed") {
1123
1101
  const msg = `Deploy failed: ${deployData.errorMessage ?? "unknown error"}`;
1124
- return mcpResult8({ action, ok: false, data }, msg, {
1102
+ return mcpResult9({ action, ok: false, data }, msg, {
1125
1103
  next: ["Run flow_validate to check your configuration"]
1126
1104
  });
1127
1105
  } else {
1128
- summary = `Deployed flow ${id} \u2014 status: ${st}`;
1106
+ summary = `Deployed flow ${flowId} \u2014 status: ${st}`;
1129
1107
  const publicUrl = deployData.publicUrl;
1130
1108
  const containerUrl = deployData.containerUrl;
1131
1109
  const deployType = deployData.type;
@@ -1138,7 +1116,7 @@ function registerApiTool(server2) {
1138
1116
  nextHints.push(`Test: curl ${containerUrl}/health`);
1139
1117
  }
1140
1118
  if (nextHints.length > 0) {
1141
- return mcpResult8({ action, ok: true, data }, summary, {
1119
+ return mcpResult9({ action, ok: true, data }, summary, {
1142
1120
  next: nextHints
1143
1121
  });
1144
1122
  }
@@ -1147,21 +1125,21 @@ function registerApiTool(server2) {
1147
1125
  }
1148
1126
  // Deployments
1149
1127
  case "deployment.get": {
1150
- if (!id)
1128
+ if (!flowId)
1151
1129
  throw new Error(
1152
- "id (flowId or slug) required for deployment.get"
1130
+ "flowId (flowId or slug) required for deployment.get"
1153
1131
  );
1154
1132
  try {
1155
- data = await getDeployment({ flowId: id, flowName });
1133
+ data = await getDeployment({ flowId, flowName });
1156
1134
  } catch {
1157
- data = await getDeploymentBySlug({ slug: id });
1135
+ data = await getDeploymentBySlug({ slug: flowId });
1158
1136
  }
1159
- summary = `Deployment ${data.slug ?? id} \u2014 ${data.status}`;
1137
+ summary = `Deployment ${data.slug ?? flowId} \u2014 ${data.status}`;
1160
1138
  break;
1161
1139
  }
1162
1140
  case "deployment.list": {
1163
1141
  data = await listDeployments({
1164
- projectId: id,
1142
+ projectId,
1165
1143
  type,
1166
1144
  status
1167
1145
  });
@@ -1173,15 +1151,15 @@ function registerApiTool(server2) {
1173
1151
  throw new Error(
1174
1152
  "type (web/server) required for deployment.create"
1175
1153
  );
1176
- data = await createDep({ type, label: name, projectId: id });
1154
+ data = await createDep({ type, label: name, projectId });
1177
1155
  summary = `Created ${type} deployment ${data.slug}`;
1178
1156
  break;
1179
1157
  }
1180
1158
  case "deployment.delete": {
1181
- if (!id)
1182
- throw new Error("id (slug) required for deployment.delete");
1183
- data = await deleteDep({ slug: id });
1184
- summary = `Deleted deployment ${id}`;
1159
+ if (!flowId)
1160
+ throw new Error("flowId (slug) required for deployment.delete");
1161
+ data = await deleteDep({ slug: flowId });
1162
+ summary = `Deleted deployment ${flowId}`;
1185
1163
  break;
1186
1164
  }
1187
1165
  default:
@@ -1189,20 +1167,36 @@ function registerApiTool(server2) {
1189
1167
  `Unknown action: ${action}. Use one of: ${ACTIONS.join(", ")}`
1190
1168
  );
1191
1169
  }
1192
- return mcpResult8({ action, ok: true, data }, summary);
1170
+ return mcpResult9({ action, ok: true, data }, summary);
1193
1171
  } catch (error) {
1194
1172
  const msg = error instanceof Error ? error.message : "";
1173
+ const name2 = error instanceof Error ? error.name : "";
1174
+ if (action === "deploy" && (name2 === "AbortError" || name2 === "TimeoutError" || msg.includes("abort"))) {
1175
+ return mcpResult9(
1176
+ {
1177
+ action,
1178
+ ok: true,
1179
+ data: { status: "deploying", flowId }
1180
+ },
1181
+ `Deploy in progress (timed out waiting). Use deployment.list with projectId to check status.`,
1182
+ {
1183
+ next: [
1184
+ 'Use api(action: "deployment.list") to check current status'
1185
+ ]
1186
+ }
1187
+ );
1188
+ }
1195
1189
  if (msg.includes("401") || msg.includes("403") || msg.includes("Unauthorized"))
1196
- return mcpError8(
1190
+ return mcpError9(
1197
1191
  error,
1198
1192
  "Set WALKEROS_TOKEN env var or check token expiry"
1199
1193
  );
1200
1194
  if (msg.includes("required"))
1201
- return mcpError8(
1195
+ return mcpError9(
1202
1196
  error,
1203
1197
  `See api tool description for ${action} parameters.`
1204
1198
  );
1205
- return mcpError8(error);
1199
+ return mcpError9(error);
1206
1200
  }
1207
1201
  }
1208
1202
  );
@@ -1213,14 +1207,17 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
1213
1207
  import { fetchPackageSchema } from "@walkeros/core";
1214
1208
  function registerPackageSchemaResources(server2) {
1215
1209
  const template = new ResourceTemplate("walkeros://schema/{packageName}", {
1216
- list: async () => ({
1217
- resources: PACKAGE_REGISTRY.map((pkg) => ({
1218
- uri: `walkeros://schema/${encodeURIComponent(pkg.name)}`,
1219
- name: pkg.name,
1220
- description: `Schema and examples for ${pkg.name}`,
1221
- mimeType: "application/json"
1222
- }))
1223
- })
1210
+ list: async () => {
1211
+ const catalog = await fetchCatalog();
1212
+ return {
1213
+ resources: catalog.map((pkg) => ({
1214
+ uri: `walkeros://schema/${encodeURIComponent(pkg.name)}`,
1215
+ name: pkg.name,
1216
+ description: `Schema and examples for ${pkg.name}`,
1217
+ mimeType: "application/json"
1218
+ }))
1219
+ };
1220
+ }
1224
1221
  });
1225
1222
  server2.registerResource(
1226
1223
  "package-schema",
@@ -1462,30 +1459,33 @@ function registerReferenceResources(server2) {
1462
1459
  description: "Complete walkerOS package catalog \u2014 all sources, destinations, transformers, and stores",
1463
1460
  mimeType: "application/json"
1464
1461
  },
1465
- async () => ({
1466
- contents: [
1467
- {
1468
- uri: "walkeros://reference/packages",
1469
- text: JSON.stringify(PACKAGE_REGISTRY, null, 2),
1470
- mimeType: "application/json"
1471
- }
1472
- ]
1473
- })
1462
+ async () => {
1463
+ const catalog = await fetchCatalog();
1464
+ return {
1465
+ contents: [
1466
+ {
1467
+ uri: "walkeros://reference/packages",
1468
+ text: JSON.stringify(catalog, null, 2),
1469
+ mimeType: "application/json"
1470
+ }
1471
+ ]
1472
+ };
1473
+ }
1474
1474
  );
1475
1475
  }
1476
1476
 
1477
1477
  // src/prompts/add-step.ts
1478
- import { z as z8 } from "zod";
1478
+ import { z as z11 } from "zod";
1479
1479
  function registerAddStepPrompt(server2) {
1480
1480
  server2.registerPrompt(
1481
1481
  "add-step",
1482
1482
  {
1483
1483
  description: "Add a source, destination, transformer, or store step to a flow configuration. Guides through package selection, config scaffolding, and wiring.",
1484
1484
  argsSchema: {
1485
- stepType: z8.string().optional().describe(
1485
+ stepType: z11.string().optional().describe(
1486
1486
  "Type of step to add: source, destination, transformer, or store"
1487
1487
  ),
1488
- flowPath: z8.string().optional().describe("Path to the flow.json file to modify")
1488
+ flowPath: z11.string().optional().describe("Path to the flow.json file to modify")
1489
1489
  }
1490
1490
  },
1491
1491
  async ({ stepType, flowPath }) => ({
@@ -1499,12 +1499,14 @@ function registerAddStepPrompt(server2) {
1499
1499
  "",
1500
1500
  "Follow these steps:",
1501
1501
  `1. ${stepType ? "" : "Ask what type of step (source, destination, transformer, store). Then "}Use package_search to browse available packages for the selected type and platform.`,
1502
- `2. Use package_get with section="hints" to read the selected package's configuration guidance.`,
1502
+ `2. Use package_get to read the package's config schema (schemas.config contains the full config shape: base fields like consent/require/logger + package-specific settings). Use section="hints" for additional guidance.`,
1503
1503
  '3. Use package_get with section="examples" to see working configuration examples.',
1504
1504
  "4. Scaffold the step config using the package schemas \u2014 include required settings with placeholder values.",
1505
1505
  "5. Wire the step into the flow: add to packages section (with version if needed), connect via next/before chains if needed.",
1506
1506
  '6. For destinations: configure mapping using nested entity \u2192 action keys. Event "product add" maps to `{ "product": { "add": { name: "AddToCart" } } }`. Use the setup-mapping prompt for guidance.',
1507
1507
  "7. Use flow_validate to verify the result.",
1508
+ "8. For server sources: check if the package supports `ingest` configuration via package_get. Ingest extracts request metadata (IP, user-agent, headers) using mapping syntax. Transformers like fingerprint depend on ingest data.",
1509
+ "9. When adding a transformer that uses ingest fields, verify the source has `ingest` configured \u2014 otherwise ingest fields resolve to empty values.",
1508
1510
  "",
1509
1511
  "Important:",
1510
1512
  "- Read the walkeros://reference/flow-schema resource to understand connection rules.",
@@ -1523,14 +1525,14 @@ function registerAddStepPrompt(server2) {
1523
1525
  }
1524
1526
 
1525
1527
  // src/prompts/setup-mapping.ts
1526
- import { z as z9 } from "zod";
1528
+ import { z as z12 } from "zod";
1527
1529
  function registerSetupMappingPrompt(server2) {
1528
1530
  server2.registerPrompt(
1529
1531
  "setup-mapping",
1530
1532
  {
1531
1533
  description: "Set up event mapping for any step in a flow. Teaches mapping syntax and uses package examples as templates.",
1532
1534
  argsSchema: {
1533
- stepName: z9.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
1535
+ stepName: z12.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
1534
1536
  }
1535
1537
  },
1536
1538
  async ({ stepName }) => ({
@@ -1542,25 +1544,15 @@ function registerSetupMappingPrompt(server2) {
1542
1544
  text: [
1543
1545
  `Help me set up mapping${stepName ? ` for the "${stepName}" step` : ""}.`,
1544
1546
  "",
1545
- "IMPORTANT \u2014 Mapping key structure:",
1546
- "Mapping uses NESTED entity \u2192 action keys, NOT dot-separated strings.",
1547
- 'Event name "product add" splits into entity "product" and action "add".',
1548
- 'Config structure: `{ "mapping": { "product": { "add": { name: "AddToCart", data: { ... } } } } }`',
1549
- 'Wildcards: `{ "*": { "view": Rule } }` matches any entity with action "view".',
1550
- "",
1551
1547
  "Follow these steps:",
1552
- "1. Read the walkeros://reference/mapping resource for full syntax reference.",
1553
- `2. ${stepName ? `Identify the package for "${stepName}" in the flow, then u` : "U"}se package_get with section="examples" to see how events are mapped for this package.`,
1554
- '3. Ask which events I want to map (e.g., "product view", "order complete").',
1555
- "4. Generate mapping rules using the package examples as templates.",
1556
- "5. Use flow_validate to verify the mapping.",
1557
- "",
1558
- "Mapping operates at two levels:",
1559
- "- **Source mapping**: normalizes raw input \u2192 walkerOS events",
1560
- "- **Destination mapping**: transforms walkerOS events \u2192 vendor format",
1548
+ "1. Read the walkeros://reference/mapping resource for syntax reference.",
1549
+ `2. ${stepName ? `Identify the package for "${stepName}" in the flow, then u` : "U"}se package_get with section="examples" to see the source output shape \u2014 mapping keys must match the actual events the source emits.`,
1550
+ "3. Ask whether this is source mapping (raw input \u2192 walkerOS events) or destination mapping (walkerOS events \u2192 vendor format).",
1551
+ "4. Ask which events to map (one at a time, not all at once).",
1552
+ "5. Generate one mapping rule using the package examples as templates. Validate it with flow_validate before moving to the next.",
1553
+ "6. Repeat for each event.",
1561
1554
  "",
1562
- "Key mapping operators: data (extract), map (object transform), loop (array processing), ",
1563
- "set (create array), fn ($code function), condition (conditional), consent (consent-gated).",
1555
+ 'Mapping uses nested entity \u2192 action keys. Event "product add" maps to `{ "product": { "add": Rule } }`. Wildcards: `{ "*": { "view": Rule } }`.',
1564
1556
  "",
1565
1557
  "Use $def references for shared mapping patterns across destinations."
1566
1558
  ].join("\n")
@@ -1572,14 +1564,14 @@ function registerSetupMappingPrompt(server2) {
1572
1564
  }
1573
1565
 
1574
1566
  // src/prompts/manage-contract.ts
1575
- import { z as z10 } from "zod";
1567
+ import { z as z13 } from "zod";
1576
1568
  function registerManageContractPrompt(server2) {
1577
1569
  server2.registerPrompt(
1578
1570
  "manage-contract",
1579
1571
  {
1580
1572
  description: "Create or update event contracts for a flow. Can generate contracts from existing mappings or scaffold mappings from contracts.",
1581
1573
  argsSchema: {
1582
- direction: z10.string().optional().describe(
1574
+ direction: z13.string().optional().describe(
1583
1575
  'Direction: "from-mappings" (extract contract from existing mappings), "from-scratch" (create new contract), or "to-mappings" (scaffold mappings from contract)'
1584
1576
  )
1585
1577
  }
@@ -1604,6 +1596,8 @@ function registerManageContractPrompt(server2) {
1604
1596
  "- **Contract \u2192 Mappings**: contract defines what events look like, mappings are scaffolded to match.",
1605
1597
  "- **Mappings \u2192 Contract**: existing mappings reveal which fields are used, contract formalizes them.",
1606
1598
  "",
1599
+ "For server flows: if the contract references fields populated by ingest (e.g., user fingerprint hash), verify the source config.ingest extracts the needed request metadata.",
1600
+ "",
1607
1601
  "Use $contract.name references to link contracts in the flow.",
1608
1602
  "Contracts support extends for inheritance between event types."
1609
1603
  ].join("\n")
@@ -1615,14 +1609,14 @@ function registerManageContractPrompt(server2) {
1615
1609
  }
1616
1610
 
1617
1611
  // src/prompts/use-definitions.ts
1618
- import { z as z11 } from "zod";
1612
+ import { z as z14 } from "zod";
1619
1613
  function registerUseDefinitionsPrompt(server2) {
1620
1614
  server2.registerPrompt(
1621
1615
  "use-definitions",
1622
1616
  {
1623
1617
  description: "Extract shared patterns into definitions and variables for DRY, environment-aware flow configurations.",
1624
1618
  argsSchema: {
1625
- flowPath: z11.string().optional().describe("Path to the flow.json file to analyze")
1619
+ flowPath: z14.string().optional().describe("Path to the flow.json file to analyze")
1626
1620
  }
1627
1621
  },
1628
1622
  async ({ flowPath }) => ({
@@ -1666,7 +1660,7 @@ function registerUseDefinitionsPrompt(server2) {
1666
1660
  var server = new McpServer(
1667
1661
  {
1668
1662
  name: "walkeros-flow",
1669
- version: "3.0.1"
1663
+ version: "3.1.0-next-1773969156384"
1670
1664
  },
1671
1665
  {
1672
1666
  instructions: `walkerOS is an open-source, privacy-first event data collection platform. Define event pipelines as code using JSON flow configurations.
@@ -1702,7 +1696,7 @@ Key rules:
1702
1696
 
1703
1697
  ## Getting Started
1704
1698
 
1705
- 1. \`flow_load({ platform: "web" })\` or \`flow_load({ source: "./flow.json" })\` \u2014 create or load a flow
1699
+ 1. \`flow_load({ platform: "web" })\` or \`flow_load({ source: "./flow.json" })\` \u2014 create or load a flow (also accepts inline JSON strings or URLs)
1706
1700
  2. \`package_search({ type: "destination", platform: "web" })\` \u2014 discover available packages
1707
1701
  3. Use the \`add-step\` prompt to add sources, destinations, transformers, or stores
1708
1702
  4. Use the \`setup-mapping\` prompt to configure event transformations
@@ -1719,7 +1713,7 @@ Read these before constructing configs manually: \`walkeros://reference/flow-sch
1719
1713
 
1720
1714
  ## Key Concepts
1721
1715
 
1722
- - **Steps** are sources, destinations, transformers, or stores \u2014 each backed by an npm package. Use \`package_search\` to browse, \`package_get\` for schemas and examples.
1716
+ - **Steps** are sources, destinations, transformers, or stores \u2014 each backed by an npm package. Use \`package_search\` to browse, \`package_get\` for schemas and examples. \`package_get\` returns \`schemas.config\` \u2014 a merged JSON Schema combining base config fields (consent, require, logger, mapping, etc.) with the package's typed settings. Non-config schemas (mapping rules, utility schemas) remain as sibling keys.
1723
1717
  - **Mapping** transforms events using data/map/loop/set/condition rules. Same syntax on sources and destinations. Mapping rules use NESTED entity \u2192 action keying: event name "product add" maps to \`{ "product": { "add": Rule } }\`. Wildcards: \`{ "*": { "view": Rule } }\`.
1724
1718
  - **Contracts** define event schemas using entity-action keying. Can generate FROM mappings or scaffold mappings FROM contracts.
1725
1719
  - **Variables** ($var, $env, $def, $code, $store) enable DRY, environment-aware config. Use the \`use-definitions\` prompt to extract shared patterns.
@@ -1734,6 +1728,7 @@ registerFlowExamplesTool(server);
1734
1728
  registerPackageSearchTool(server);
1735
1729
  registerGetPackageSchemaTool(server);
1736
1730
  registerFlowLoadTool(server);
1731
+ registerFeedbackTool(server);
1737
1732
  registerPackageSchemaResources(server);
1738
1733
  registerReferenceResources(server);
1739
1734
  registerAddStepPrompt(server);