@walkeros/mcp 3.4.2 → 4.0.0-next-1777463920154

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 ADDED
@@ -0,0 +1,2838 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/stdio.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { setClientContext } from "@walkeros/cli";
6
+
7
+ // src/server.ts
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+
10
+ // src/tools/validate.ts
11
+ import { validate } from "@walkeros/cli";
12
+ import { schemas } from "@walkeros/cli/dev";
13
+ import { mcpResult, mcpError } from "@walkeros/core";
14
+
15
+ // src/schemas/output.ts
16
+ import { z } from "zod";
17
+ var ValidateOutputShape = {
18
+ valid: z.boolean().describe("Whether validation passed"),
19
+ type: z.union([
20
+ z.enum(["contract", "entry", "event", "flow", "mapping"]),
21
+ z.string().regex(/^(destinations|sources|transformers)\.\w+$/)
22
+ ]).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"),
31
+ warnings: z.array(
32
+ z.object({
33
+ path: z.string(),
34
+ message: z.string(),
35
+ suggestion: z.string().optional()
36
+ })
37
+ ).describe("Validation warnings"),
38
+ details: z.record(z.string(), z.unknown()).describe("Additional validation details")
39
+ };
40
+ var BundleOutputShape = {
41
+ success: z.boolean().describe("Whether bundling succeeded"),
42
+ totalSize: z.number().optional().describe("Total bundle size in bytes"),
43
+ buildTime: z.number().optional().describe("Build time in milliseconds"),
44
+ packages: z.array(
45
+ z.object({
46
+ name: z.string(),
47
+ size: z.number()
48
+ })
49
+ ).optional().describe("Per-package size breakdown"),
50
+ treeshakingEffective: z.boolean().optional().describe("Whether tree-shaking was effective"),
51
+ message: z.string().optional().describe("Status message")
52
+ };
53
+ var SimulateOutputShape = {
54
+ success: z.boolean().describe("Whether simulation succeeded"),
55
+ error: z.string().optional().describe("Error message if failed"),
56
+ summary: z.string().describe("One-line result summary"),
57
+ destinations: z.record(
58
+ z.string(),
59
+ z.object({
60
+ received: z.boolean().describe("Whether destination received the event"),
61
+ calls: z.number().describe("Number of API calls made"),
62
+ payload: z.unknown().optional().describe("All intercepted API calls (only when verbose: true)")
63
+ })
64
+ ).optional().describe("Per-destination results"),
65
+ capturedEvents: z.array(z.record(z.string(), z.unknown())).optional().describe("Events captured by source simulation"),
66
+ duration: z.number().optional().describe("Simulation duration in ms")
67
+ };
68
+ var PushOutputShape = {
69
+ success: z.boolean().describe("Whether push succeeded"),
70
+ elbResult: z.unknown().optional().describe("Push result from the collector"),
71
+ duration: z.number().describe("Push duration in milliseconds"),
72
+ error: z.string().optional().describe("Error message if push failed")
73
+ };
74
+ var ExamplesListOutputShape = {
75
+ flow: z.string().describe("Flow name"),
76
+ count: z.number().describe("Number of examples found"),
77
+ examples: z.array(
78
+ z.object({
79
+ step: z.string().describe('Step location (e.g., "destination.gtag")'),
80
+ stepType: z.enum(["source", "transformer", "destination"]).describe("Step type"),
81
+ stepName: z.string().describe("Step name"),
82
+ exampleName: z.string().describe("Example name"),
83
+ title: z.string().optional().describe("Human-readable title if set"),
84
+ description: z.string().optional().describe("Short human-readable description"),
85
+ public: z.boolean().optional().describe(
86
+ "Whether the example is public (defaults to true if omitted)"
87
+ ),
88
+ hasIn: z.boolean().describe("Whether the example has an input value"),
89
+ hasOut: z.boolean().describe("Whether the example has an output value"),
90
+ hasMapping: z.boolean().describe("Whether the example has a mapping configuration"),
91
+ hasTrigger: z.boolean().describe("Whether the example has trigger metadata"),
92
+ in: z.unknown().optional().describe("Input event data"),
93
+ out: z.unknown().optional().describe("Expected output data"),
94
+ mapping: z.unknown().optional().describe("Mapping configuration for destinations"),
95
+ trigger: z.object({
96
+ type: z.string().optional(),
97
+ options: z.unknown().optional()
98
+ }).optional().describe("Trigger metadata for source simulation")
99
+ })
100
+ ).describe("Step examples")
101
+ };
102
+
103
+ // src/user-data.ts
104
+ function wrapUserData(s) {
105
+ const alreadyWrapped = s.startsWith("<user_data>") && s.endsWith("</user_data>") && s.slice(11, -12).indexOf("</user_data>") === -1;
106
+ if (alreadyWrapped) return s;
107
+ const neutralised = s.replace(/<\/user_data>/g, "</user_data_>");
108
+ return `<user_data>${neutralised}</user_data>`;
109
+ }
110
+ function redactNestedStrings(value, opts) {
111
+ return walk(value, opts);
112
+ }
113
+ function walk(value, opts) {
114
+ if (typeof value === "string") return wrapUserData(value);
115
+ if (Array.isArray(value)) return value.map((v) => walk(v, opts));
116
+ if (value && typeof value === "object") {
117
+ const out = {};
118
+ for (const [k, v] of Object.entries(value)) {
119
+ if (opts?.skip?.(k) && typeof v === "string") {
120
+ out[k] = v;
121
+ } else {
122
+ out[k] = walk(v, opts);
123
+ }
124
+ }
125
+ return out;
126
+ }
127
+ return value;
128
+ }
129
+
130
+ // src/tools/validate.ts
131
+ function wrapIssueMessages(result) {
132
+ return {
133
+ ...result,
134
+ errors: result.errors.map((e) => ({
135
+ ...e,
136
+ message: wrapUserData(e.message)
137
+ })),
138
+ warnings: result.warnings.map((w) => ({
139
+ ...w,
140
+ message: wrapUserData(w.message)
141
+ }))
142
+ };
143
+ }
144
+ var TITLE = "Validate Flow";
145
+ var DESCRIPTION = "Validate walkerOS events, flow configurations, mapping rules, or data contracts. Accepts JSON strings, file paths, or URLs as input. Returns validation results with errors, warnings, and details.";
146
+ var inputSchema = schemas.ValidateInputShape;
147
+ var annotations = {
148
+ readOnlyHint: true,
149
+ destructiveHint: false,
150
+ idempotentHint: true,
151
+ openWorldHint: false
152
+ };
153
+ function createFlowValidateToolSpec() {
154
+ return {
155
+ name: "flow_validate",
156
+ title: TITLE,
157
+ description: DESCRIPTION,
158
+ inputSchema,
159
+ annotations,
160
+ handler: (input) => flowValidateHandlerBody(input)
161
+ };
162
+ }
163
+ async function flowValidateHandlerBody(input) {
164
+ const {
165
+ type,
166
+ input: validateInput,
167
+ flow,
168
+ path
169
+ } = input ?? {};
170
+ try {
171
+ const result = await validate(type, validateInput, {
172
+ flow,
173
+ path
174
+ });
175
+ const hints = result.valid ? {
176
+ next: [
177
+ "Use flow_simulate to test event flow",
178
+ "Use flow_bundle to build"
179
+ ]
180
+ } : {
181
+ next: [
182
+ "Fix errors above, then run flow_validate again",
183
+ "Read walkeros://reference/flow-schema for correct structure"
184
+ ]
185
+ };
186
+ return mcpResult(wrapIssueMessages(result), hints);
187
+ } catch (error) {
188
+ return mcpError(
189
+ error,
190
+ "Check the input parameter \u2014 expected a JSON string, file path, or URL"
191
+ );
192
+ }
193
+ }
194
+ function registerFlowValidateTool(server) {
195
+ const spec = createFlowValidateToolSpec();
196
+ server.registerTool(
197
+ spec.name,
198
+ {
199
+ title: spec.title,
200
+ description: spec.description,
201
+ inputSchema: spec.inputSchema,
202
+ // outputSchema is wire-only; not part of ToolSpec
203
+ outputSchema: ValidateOutputShape,
204
+ annotations: spec.annotations
205
+ },
206
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
207
+ // type-erased (input: unknown) => Promise<unknown> form by design.
208
+ spec.handler
209
+ );
210
+ }
211
+
212
+ // src/tools/bundle.ts
213
+ import { bundle } from "@walkeros/cli";
214
+ import { schemas as schemas2 } from "@walkeros/cli/dev";
215
+ import { mcpResult as mcpResult2, mcpError as mcpError2 } from "@walkeros/core";
216
+ var TITLE2 = "Bundle Flow";
217
+ var DESCRIPTION2 = "Bundle a walkerOS flow configuration into deployable JavaScript. Resolves all destinations, sources, and transformers, then outputs a tree-shaken production bundle. Returns bundle statistics.";
218
+ var inputSchema2 = {
219
+ ...schemas2.BundleInputShape
220
+ };
221
+ var annotations2 = {
222
+ readOnlyHint: false,
223
+ destructiveHint: false,
224
+ idempotentHint: false,
225
+ openWorldHint: true
226
+ };
227
+ function createFlowBundleToolSpec() {
228
+ return {
229
+ name: "flow_bundle",
230
+ title: TITLE2,
231
+ description: DESCRIPTION2,
232
+ inputSchema: inputSchema2,
233
+ annotations: annotations2,
234
+ handler: (input) => flowBundleHandlerBody(input)
235
+ };
236
+ }
237
+ async function flowBundleHandlerBody(input) {
238
+ const { configPath, flow, stats, output } = input ?? {};
239
+ try {
240
+ const result = await bundle(configPath, {
241
+ flowName: flow,
242
+ stats: stats ?? true,
243
+ buildOverrides: output ? { output } : void 0
244
+ });
245
+ if (!result) {
246
+ return mcpResult2(
247
+ { success: false, message: "Bundle produced no output" },
248
+ {
249
+ warnings: [
250
+ "The build returned no result. The flow may be empty or misconfigured."
251
+ ],
252
+ next: ["Run flow_validate to check your configuration"]
253
+ }
254
+ );
255
+ }
256
+ const output_ = result;
257
+ return mcpResult2(
258
+ { success: true, ...output_ },
259
+ {
260
+ next: [
261
+ "Use flow_simulate to test",
262
+ 'Use deploy_manage with action "deploy" to publish'
263
+ ]
264
+ }
265
+ );
266
+ } catch (error) {
267
+ return mcpError2(error, "Run flow_validate for detailed error messages");
268
+ }
269
+ }
270
+ function registerFlowBundleTool(server) {
271
+ const spec = createFlowBundleToolSpec();
272
+ server.registerTool(
273
+ spec.name,
274
+ {
275
+ title: spec.title,
276
+ description: spec.description,
277
+ inputSchema: spec.inputSchema,
278
+ // outputSchema is wire-only; not part of ToolSpec
279
+ outputSchema: BundleOutputShape,
280
+ annotations: spec.annotations
281
+ },
282
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
283
+ // type-erased (input: unknown) => Promise<unknown> form by design.
284
+ spec.handler
285
+ );
286
+ }
287
+
288
+ // src/tools/simulate.ts
289
+ import { z as z2 } from "zod";
290
+ import {
291
+ simulateSource,
292
+ simulateTransformer,
293
+ simulateDestination
294
+ } from "@walkeros/cli";
295
+ import { schemas as schemas3 } from "@walkeros/cli/dev";
296
+ import { mcpResult as mcpResult3, mcpError as mcpError3 } from "@walkeros/core";
297
+ var TITLE3 = "Simulate Flow";
298
+ var DESCRIPTION3 = '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. IMPORTANT: Destinations with require (e.g. require: ["consent"]) stay pending until that collector event fires \u2014 simulation will error "not found" if require is not satisfied. Remove require from config or provide consent/user events before simulating. Separately, destinations with consent (e.g. consent: { marketing: true }) only receive events where the event includes matching consent. Mapping transforms event names and data at the destination level. Policy redacts or injects fields before mapping runs.';
299
+ var inputSchema3 = {
300
+ configPath: schemas3.SimulateInputShape.configPath,
301
+ event: z2.union([z2.record(z2.string(), z2.unknown()), z2.string()]).optional().describe(
302
+ "For destinations: { name, data, consent? }. Include consent (e.g. { marketing: true }) to satisfy destination consent requirements. For sources: { content, trigger?, env? }. Can also be a JSON string or file path."
303
+ ),
304
+ flow: schemas3.SimulateInputShape.flow,
305
+ platform: schemas3.SimulateInputShape.platform,
306
+ step: schemas3.SimulateInputShape.step,
307
+ verbose: z2.boolean().optional().describe("Include full payload per destination (default: false)")
308
+ };
309
+ var annotations3 = {
310
+ readOnlyHint: true,
311
+ destructiveHint: false,
312
+ idempotentHint: true,
313
+ openWorldHint: false
314
+ };
315
+ function createFlowSimulateToolSpec() {
316
+ return {
317
+ name: "flow_simulate",
318
+ title: TITLE3,
319
+ description: DESCRIPTION3,
320
+ inputSchema: inputSchema3,
321
+ annotations: annotations3,
322
+ handler: (input) => flowSimulateHandlerBody(input)
323
+ };
324
+ }
325
+ async function flowSimulateHandlerBody(input) {
326
+ const { configPath, event, flow, platform, step, verbose } = input ?? {};
327
+ try {
328
+ if (!event) {
329
+ throw new Error(
330
+ "event is required. For sources provide { content, trigger? }, for destinations provide { name, data }."
331
+ );
332
+ }
333
+ if (!step) {
334
+ throw new Error(
335
+ 'step is required. Specify a target like "source.browser", "destination.gtag", or "transformer.demo".'
336
+ );
337
+ }
338
+ let resolvedEvent = event;
339
+ if (typeof event === "string") {
340
+ try {
341
+ resolvedEvent = JSON.parse(event);
342
+ } catch {
343
+ throw new Error(
344
+ "Event string must be valid JSON. Got: " + event.substring(0, 50)
345
+ );
346
+ }
347
+ }
348
+ const dotIndex = step.indexOf(".");
349
+ if (dotIndex === -1) {
350
+ throw new Error(
351
+ `Invalid step format "${step}". Use "type.name" (e.g. "source.browser", "destination.gtag").`
352
+ );
353
+ }
354
+ const stepType = step.substring(0, dotIndex);
355
+ const stepId = step.substring(dotIndex + 1);
356
+ let result;
357
+ switch (stepType) {
358
+ case "source":
359
+ result = await simulateSource(configPath, resolvedEvent, {
360
+ sourceId: stepId,
361
+ flow,
362
+ silent: true
363
+ });
364
+ break;
365
+ case "transformer":
366
+ result = await simulateTransformer(
367
+ configPath,
368
+ resolvedEvent,
369
+ {
370
+ transformerId: stepId,
371
+ flow,
372
+ silent: true
373
+ }
374
+ );
375
+ break;
376
+ case "destination":
377
+ result = await simulateDestination(
378
+ configPath,
379
+ resolvedEvent,
380
+ {
381
+ destinationId: stepId,
382
+ flow,
383
+ silent: true
384
+ }
385
+ );
386
+ break;
387
+ default:
388
+ throw new Error(
389
+ `Unknown step type "${stepType}". Use "source", "transformer", or "destination".`
390
+ );
391
+ }
392
+ if (result.captured && result.captured.length > 0) {
393
+ const eventCount = result.captured.length;
394
+ const summary2 = `Source captured ${eventCount} event${eventCount !== 1 ? "s" : ""}`;
395
+ return mcpResult3(
396
+ {
397
+ success: result.success,
398
+ error: result.error,
399
+ summary: summary2,
400
+ capturedEvents: result.captured,
401
+ duration: result.duration
402
+ },
403
+ {
404
+ next: eventCount > 0 ? [
405
+ "Use flow_simulate with a destination step to test downstream processing"
406
+ ] : [
407
+ "Check source package examples with package_get, verify trigger type matches"
408
+ ]
409
+ }
410
+ );
411
+ }
412
+ const destinations = {};
413
+ if (result.elbResult && typeof result.elbResult === "object" && "done" in result.elbResult && result.elbResult.done) {
414
+ const done = result.elbResult.done;
415
+ for (const name of Object.keys(done)) {
416
+ destinations[name] = { received: true, calls: 0 };
417
+ }
418
+ }
419
+ if (result.usage) {
420
+ for (const [name, calls] of Object.entries(result.usage)) {
421
+ const summary2 = {
422
+ received: calls.length > 0,
423
+ calls: calls.length
424
+ };
425
+ if (verbose && calls.length > 0) {
426
+ summary2.payload = calls;
427
+ }
428
+ destinations[name] = summary2;
429
+ }
430
+ }
431
+ const destCount = Object.keys(destinations).length;
432
+ const receivedCount = Object.values(destinations).filter(
433
+ (d) => d.received
434
+ ).length;
435
+ const warnings = [];
436
+ if (stepType === "destination" && destCount === 0) {
437
+ warnings.push(
438
+ 'Destination did not receive the event. Common causes: (1) destination config has consent: { marketing: true } but event lacks matching consent, (2) mapping rules do not match the event name, (3) policy redacted required fields. Add consent to the event: { name: "...", data: {...}, consent: { marketing: true } }.'
439
+ );
440
+ }
441
+ const summary = stepType === "transformer" ? `Transformer processed event` : `${receivedCount}/${destCount} destinations received the event`;
442
+ const resultObj = {
443
+ success: result.success,
444
+ error: result.error,
445
+ summary,
446
+ destinations: destCount > 0 ? destinations : void 0,
447
+ duration: result.duration
448
+ };
449
+ return mcpResult3(resultObj, {
450
+ next: ["Use flow_bundle to build for production"],
451
+ ...warnings.length > 0 ? { warnings } : {}
452
+ });
453
+ } catch (error) {
454
+ const msg = error instanceof Error ? error.message : "";
455
+ let hint = "Run flow_validate for detailed error messages";
456
+ if (msg.includes("not found in collector")) {
457
+ hint = 'If this destination has require: ["consent"] or require: ["user"], it stays pending until that event fires. For simulation, either remove require from the config or simulate with a flow that omits require on the target destination.';
458
+ }
459
+ return mcpError3(error, hint);
460
+ }
461
+ }
462
+ function registerFlowSimulateTool(server) {
463
+ const spec = createFlowSimulateToolSpec();
464
+ server.registerTool(
465
+ spec.name,
466
+ {
467
+ title: spec.title,
468
+ description: spec.description,
469
+ inputSchema: spec.inputSchema,
470
+ // outputSchema is wire-only; not part of ToolSpec
471
+ outputSchema: SimulateOutputShape,
472
+ annotations: spec.annotations
473
+ },
474
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
475
+ // type-erased (input: unknown) => Promise<unknown> form by design.
476
+ spec.handler
477
+ );
478
+ }
479
+
480
+ // src/tools/push.ts
481
+ import { z as z3 } from "zod";
482
+ import { push } from "@walkeros/cli";
483
+ import { schemas as schemas4 } from "@walkeros/cli/dev";
484
+ import { mcpResult as mcpResult4, mcpError as mcpError4 } from "@walkeros/core";
485
+ var TITLE4 = "Push Events";
486
+ var DESCRIPTION4 = "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.";
487
+ var inputSchema4 = {
488
+ configPath: schemas4.PushInputShape.configPath,
489
+ event: z3.record(z3.string(), z3.unknown()).describe(
490
+ 'Event object, e.g. { name: "page view", data: { title: "Home" } }'
491
+ ),
492
+ flow: schemas4.PushInputShape.flow,
493
+ platform: schemas4.PushInputShape.platform
494
+ };
495
+ var annotations4 = {
496
+ readOnlyHint: false,
497
+ destructiveHint: true,
498
+ idempotentHint: false,
499
+ openWorldHint: true
500
+ };
501
+ function createFlowPushToolSpec() {
502
+ return {
503
+ name: "flow_push",
504
+ title: TITLE4,
505
+ description: DESCRIPTION4,
506
+ inputSchema: inputSchema4,
507
+ annotations: annotations4,
508
+ handler: (input) => flowPushHandlerBody(input)
509
+ };
510
+ }
511
+ async function flowPushHandlerBody(input) {
512
+ const { configPath, event, flow, platform } = input ?? {};
513
+ try {
514
+ const result = await push(configPath, event, {
515
+ json: true,
516
+ flow,
517
+ platform
518
+ });
519
+ if (!result.success) {
520
+ return mcpError4(
521
+ new Error(result.error || "Push failed"),
522
+ "Check destination configuration and connectivity."
523
+ );
524
+ }
525
+ return mcpResult4(result);
526
+ } catch (error) {
527
+ return mcpError4(
528
+ error,
529
+ "Check configPath and event format. For web flows, use flow_simulate."
530
+ );
531
+ }
532
+ }
533
+ function registerFlowPushTool(server) {
534
+ const spec = createFlowPushToolSpec();
535
+ server.registerTool(
536
+ spec.name,
537
+ {
538
+ title: spec.title,
539
+ description: spec.description,
540
+ inputSchema: spec.inputSchema,
541
+ // outputSchema is wire-only; not part of ToolSpec
542
+ outputSchema: PushOutputShape,
543
+ annotations: spec.annotations
544
+ },
545
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
546
+ // type-erased (input: unknown) => Promise<unknown> form by design.
547
+ spec.handler
548
+ );
549
+ }
550
+
551
+ // src/tools/examples.ts
552
+ import { z as z4 } from "zod";
553
+ import { loadJsonConfig } from "@walkeros/cli";
554
+ import { mcpResult as mcpResult5, mcpError as mcpError5 } from "@walkeros/core";
555
+ var TITLE5 = "Flow Examples";
556
+ var DESCRIPTION5 = "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.";
557
+ var inputSchema5 = {
558
+ configPath: z4.string().min(1).describe("Path to flow configuration file, URL, or inline JSON string"),
559
+ flow: z4.string().optional().describe("Flow name for multi-flow configs"),
560
+ step: z4.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
561
+ full: z4.boolean().optional().describe(
562
+ "Return full in/out/mapping data for each example (default: false, returns metadata only)"
563
+ ),
564
+ includeHidden: z4.boolean().optional().describe(
565
+ "Include examples marked public: false (default: false). Set true for test/debug discovery."
566
+ )
567
+ };
568
+ var annotations5 = {
569
+ readOnlyHint: true,
570
+ destructiveHint: false,
571
+ idempotentHint: true,
572
+ openWorldHint: false
573
+ };
574
+ function createFlowExamplesToolSpec() {
575
+ return {
576
+ name: "flow_examples",
577
+ title: TITLE5,
578
+ description: DESCRIPTION5,
579
+ inputSchema: inputSchema5,
580
+ annotations: annotations5,
581
+ handler: (input) => flowExamplesHandlerBody(input)
582
+ };
583
+ }
584
+ async function flowExamplesHandlerBody(input) {
585
+ const { configPath, flow, step, full, includeHidden } = input ?? {};
586
+ try {
587
+ const rawConfig = await loadJsonConfig(configPath);
588
+ const flowNames = Object.keys(rawConfig.flows || {});
589
+ const flowName = flow || (flowNames.length === 1 ? flowNames[0] : void 0);
590
+ if (!flowName) {
591
+ throw new Error(
592
+ `Multiple flows found. Specify flow parameter. Available: ${flowNames.join(", ")}`
593
+ );
594
+ }
595
+ const flowSettings = rawConfig.flows[flowName];
596
+ if (!flowSettings) {
597
+ throw new Error(`Flow "${flowName}" not found`);
598
+ }
599
+ const examples = [];
600
+ const stepTypes = [
601
+ { key: "sources", type: "source" },
602
+ { key: "transformers", type: "transformer" },
603
+ { key: "destinations", type: "destination" }
604
+ ];
605
+ for (const { key, type } of stepTypes) {
606
+ const refs = flowSettings[key] || {};
607
+ for (const [name, ref] of Object.entries(refs)) {
608
+ if (!ref.examples) continue;
609
+ if (step && `${type}.${name}` !== step) continue;
610
+ for (const [exName, ex] of Object.entries(
611
+ ref.examples
612
+ )) {
613
+ if (!includeHidden && ex.public === false) continue;
614
+ examples.push({
615
+ step: `${type}.${name}`,
616
+ stepType: type,
617
+ stepName: name,
618
+ exampleName: exName,
619
+ title: ex.title,
620
+ description: ex.description,
621
+ public: ex.public,
622
+ hasIn: ex.in !== void 0,
623
+ hasOut: ex.out !== void 0,
624
+ hasMapping: ex.mapping !== void 0,
625
+ hasTrigger: ex.trigger !== void 0,
626
+ ...full ? {
627
+ in: ex.in,
628
+ out: ex.out,
629
+ mapping: ex.mapping,
630
+ trigger: ex.trigger
631
+ } : {}
632
+ });
633
+ }
634
+ }
635
+ }
636
+ const result = {
637
+ flow: flowName,
638
+ count: examples.length,
639
+ examples
640
+ };
641
+ const hints = {
642
+ next: ["Use flow_simulate with step and event to simulate"]
643
+ };
644
+ if (examples.length === 0) {
645
+ hints.warnings = [
646
+ "No examples found. Add examples to step definitions in your flow config for testing."
647
+ ];
648
+ }
649
+ return mcpResult5(result, hints);
650
+ } catch (error) {
651
+ return mcpError5(error, "Check configPath \u2014 expected a flow.json file");
652
+ }
653
+ }
654
+ function registerFlowExamplesTool(server) {
655
+ const spec = createFlowExamplesToolSpec();
656
+ server.registerTool(
657
+ spec.name,
658
+ {
659
+ title: spec.title,
660
+ description: spec.description,
661
+ inputSchema: spec.inputSchema,
662
+ // outputSchema is wire-only; not part of ToolSpec
663
+ outputSchema: ExamplesListOutputShape,
664
+ annotations: spec.annotations
665
+ },
666
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
667
+ // type-erased (input: unknown) => Promise<unknown> form by design.
668
+ spec.handler
669
+ );
670
+ }
671
+
672
+ // src/tools/package.ts
673
+ import { z as z5 } from "zod";
674
+ import { fetchPackage, mcpResult as mcpResult6, mcpError as mcpError6 } from "@walkeros/core";
675
+ import { mergeConfigSchema } from "@walkeros/core/dev";
676
+
677
+ // src/catalog.ts
678
+ var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
679
+ var JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm";
680
+ var WALKEROS_JSON_PATH = "dist/walkerOS.json";
681
+ var CACHE_TTL = 5 * 60 * 1e3;
682
+ var CLIENT_HEADER = "walkeros-mcp/4.0.0-next-1777463920154";
683
+ var cache;
684
+ function normalizePlatform(platform) {
685
+ if (platform == null) return [];
686
+ if (typeof platform === "string") {
687
+ return platform === "universal" ? ["web", "server"] : [platform];
688
+ }
689
+ if (Array.isArray(platform)) {
690
+ return platform.filter((v) => typeof v === "string");
691
+ }
692
+ return [];
693
+ }
694
+ async function fetchCatalog(filters) {
695
+ if (cache && Date.now() - cache.timestamp < CACHE_TTL) {
696
+ return applyFilters(cache.entries, filters);
697
+ }
698
+ let entries;
699
+ try {
700
+ entries = filters?.baseUrl ? await fetchCatalogFrom(filters.baseUrl, filters) : await fetchFromNpm();
701
+ } catch {
702
+ try {
703
+ entries = await fetchFromNpm();
704
+ } catch {
705
+ return [];
706
+ }
707
+ }
708
+ cache = { entries, timestamp: Date.now() };
709
+ return applyFilters(entries, filters);
710
+ }
711
+ async function fetchCatalogFrom(baseUrl, filters) {
712
+ const params = new URLSearchParams();
713
+ if (filters?.type) params.set("type", filters.type);
714
+ if (filters?.platform) params.set("platform", filters.platform);
715
+ const url = `${baseUrl}/api/packages${params.toString() ? `?${params}` : ""}`;
716
+ const res = await fetch(url, {
717
+ signal: AbortSignal.timeout(15e3),
718
+ headers: { "X-Walkeros-Client": CLIENT_HEADER }
719
+ });
720
+ if (!res.ok) throw new Error(`Catalog fetch failed: ${res.status}`);
721
+ const data = await res.json();
722
+ return data.catalog;
723
+ }
724
+ async function fetchFromNpm() {
725
+ const res = await fetch(`${NPM_SEARCH_URL}?text=@walkeros/&size=250`, {
726
+ signal: AbortSignal.timeout(1e4),
727
+ headers: { "X-Walkeros-Client": CLIENT_HEADER }
728
+ });
729
+ if (!res.ok) throw new Error(`npm search failed: ${res.status}`);
730
+ const data = await res.json();
731
+ const metaResults = await Promise.allSettled(
732
+ data.objects.map((obj) => enrichWithMeta(obj.package))
733
+ );
734
+ return metaResults.filter(
735
+ (r) => r.status === "fulfilled"
736
+ ).map((r) => r.value).filter((entry) => entry !== void 0);
737
+ }
738
+ async function enrichWithMeta(pkg) {
739
+ try {
740
+ const res = await fetch(
741
+ `${JSDELIVR_BASE}/${pkg.name}@${pkg.version}/${WALKEROS_JSON_PATH}`,
742
+ {
743
+ signal: AbortSignal.timeout(5e3),
744
+ headers: { "X-Walkeros-Client": CLIENT_HEADER }
745
+ }
746
+ );
747
+ if (!res.ok) return void 0;
748
+ const json = await res.json();
749
+ const meta = json.$meta;
750
+ if (!meta || typeof meta.type !== "string") return void 0;
751
+ return {
752
+ name: pkg.name,
753
+ version: pkg.version,
754
+ description: pkg.description,
755
+ type: meta.type,
756
+ platform: normalizePlatform(meta.platform)
757
+ };
758
+ } catch {
759
+ return void 0;
760
+ }
761
+ }
762
+ function applyFilters(entries, filters) {
763
+ let results = entries;
764
+ if (filters?.type) {
765
+ results = results.filter((e) => e.type === filters.type);
766
+ }
767
+ if (filters?.platform) {
768
+ results = results.filter(
769
+ (e) => e.platform.length === 0 || e.platform.includes(filters.platform)
770
+ );
771
+ }
772
+ return results;
773
+ }
774
+
775
+ // src/tools/package.ts
776
+ var SEARCH_TITLE = "Search Package";
777
+ 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.";
778
+ var searchInputSchema = {
779
+ package: z5.string().min(1).optional().describe(
780
+ "Exact npm package name for detailed lookup (e.g., @walkeros/web-destination-snowplow)"
781
+ ),
782
+ type: z5.enum(["source", "destination", "transformer", "store"]).optional().describe("Filter by package type (browse mode)"),
783
+ platform: z5.enum(["web", "server"]).optional().describe("Filter by platform (browse mode, includes universal packages)"),
784
+ version: z5.string().optional().describe("Package version for detailed lookup (default: latest)")
785
+ };
786
+ var searchAnnotations = {
787
+ readOnlyHint: true,
788
+ destructiveHint: false,
789
+ idempotentHint: true,
790
+ openWorldHint: false
791
+ };
792
+ function createPackageSearchToolSpec() {
793
+ return {
794
+ name: "package_search",
795
+ title: SEARCH_TITLE,
796
+ description: SEARCH_DESCRIPTION,
797
+ inputSchema: searchInputSchema,
798
+ annotations: searchAnnotations,
799
+ handler: (input) => packageSearchHandlerBody(input)
800
+ };
801
+ }
802
+ async function packageSearchHandlerBody(input) {
803
+ const {
804
+ package: packageName,
805
+ type,
806
+ platform,
807
+ version
808
+ } = input ?? {};
809
+ const baseUrl = process.env.APP_URL || void 0;
810
+ if (!packageName) {
811
+ const catalog = await fetchCatalog({ type, platform, baseUrl });
812
+ const result = { catalog, count: catalog.length };
813
+ return mcpResult6(result, {
814
+ next: ["Use package_get for schemas and examples"]
815
+ });
816
+ }
817
+ try {
818
+ const info = await fetchPackage(packageName, {
819
+ version,
820
+ baseUrl,
821
+ client: CLIENT_HEADER
822
+ });
823
+ const result = {
824
+ package: info.packageName,
825
+ version: info.version,
826
+ description: info.description,
827
+ type: info.type,
828
+ platform: normalizePlatform(info.platform),
829
+ hintKeys: info.hintKeys,
830
+ exampleSummaries: info.exampleSummaries
831
+ };
832
+ return mcpResult6(result, {
833
+ next: ["Use package_get for schemas and examples"]
834
+ });
835
+ } catch (error) {
836
+ return mcpError6(
837
+ error,
838
+ "Package not found. Use package_search without parameters to browse available packages."
839
+ );
840
+ }
841
+ }
842
+ function registerPackageSearchTool(server) {
843
+ const spec = createPackageSearchToolSpec();
844
+ server.registerTool(
845
+ spec.name,
846
+ {
847
+ title: spec.title,
848
+ description: spec.description,
849
+ inputSchema: spec.inputSchema,
850
+ // No outputSchema: browse mode returns {catalog, count}, lookup returns metadata — incompatible shapes
851
+ annotations: spec.annotations
852
+ },
853
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
854
+ // type-erased (input: unknown) => Promise<unknown> form by design.
855
+ spec.handler
856
+ );
857
+ }
858
+ var GET_TITLE = "Get Package";
859
+ 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".';
860
+ var getInputSchema = {
861
+ package: z5.string().min(1).describe(
862
+ "Exact npm package name (e.g., @walkeros/web-destination-snowplow)"
863
+ ),
864
+ version: z5.string().optional().describe("Package version (default: latest)"),
865
+ section: z5.enum(["hints", "examples", "all"]).optional().describe(
866
+ "Section to expand with full content. Default: summary view with schemas + hint texts + example descriptions"
867
+ )
868
+ };
869
+ var getAnnotations = {
870
+ readOnlyHint: true,
871
+ destructiveHint: false,
872
+ idempotentHint: true,
873
+ openWorldHint: true
874
+ };
875
+ function createPackageGetToolSpec() {
876
+ return {
877
+ name: "package_get",
878
+ title: GET_TITLE,
879
+ description: GET_DESCRIPTION,
880
+ inputSchema: getInputSchema,
881
+ annotations: getAnnotations,
882
+ handler: (input) => packageGetHandlerBody(input)
883
+ };
884
+ }
885
+ async function packageGetHandlerBody(input) {
886
+ const {
887
+ package: packageName,
888
+ version,
889
+ section
890
+ } = input ?? {};
891
+ const baseUrl = process.env.APP_URL || void 0;
892
+ try {
893
+ const info = await fetchPackage(packageName, {
894
+ version,
895
+ baseUrl,
896
+ client: CLIENT_HEADER
897
+ });
898
+ const mergedSchemas = {};
899
+ if (info.type) {
900
+ mergedSchemas.config = mergeConfigSchema(
901
+ info.type,
902
+ info.schemas
903
+ );
904
+ }
905
+ for (const [key, value] of Object.entries(info.schemas)) {
906
+ if (key !== "settings") {
907
+ mergedSchemas[key] = value;
908
+ }
909
+ }
910
+ const result = {
911
+ package: info.packageName,
912
+ version: info.version,
913
+ type: info.type,
914
+ platform: normalizePlatform(info.platform),
915
+ schemas: mergedSchemas
916
+ };
917
+ if (info.hints) {
918
+ if (section === "hints" || section === "all") {
919
+ result.hints = info.hints;
920
+ } else {
921
+ const hintSummary = {};
922
+ for (const [key, hint] of Object.entries(info.hints)) {
923
+ const h = hint;
924
+ hintSummary[key] = { text: h.text };
925
+ }
926
+ result.hints = hintSummary;
927
+ }
928
+ }
929
+ if (section === "examples" || section === "all") {
930
+ result.examples = info.examples;
931
+ } else {
932
+ result.exampleSummaries = info.exampleSummaries;
933
+ }
934
+ return mcpResult6(result);
935
+ } catch (error) {
936
+ return mcpError6(
937
+ error,
938
+ "Use package_search to browse available package names."
939
+ );
940
+ }
941
+ }
942
+ function registerGetPackageSchemaTool(server) {
943
+ const spec = createPackageGetToolSpec();
944
+ server.registerTool(
945
+ spec.name,
946
+ {
947
+ title: spec.title,
948
+ description: spec.description,
949
+ inputSchema: spec.inputSchema,
950
+ // No outputSchema: removed to avoid SDK -32602 crashes on unexpected field values
951
+ annotations: spec.annotations
952
+ },
953
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
954
+ // type-erased (input: unknown) => Promise<unknown> form by design.
955
+ spec.handler
956
+ );
957
+ }
958
+
959
+ // src/tools/flow-load.ts
960
+ import { z as z6 } from "zod";
961
+ import { loadJsonConfig as loadJsonConfig2 } from "@walkeros/cli";
962
+ import { mcpResult as mcpResult7, mcpError as mcpError7 } from "@walkeros/core";
963
+ var KEEP_LITERAL = /* @__PURE__ */ new Set([
964
+ "id",
965
+ "flowId",
966
+ "projectId",
967
+ "version",
968
+ "slug",
969
+ "createdAt",
970
+ "updatedAt",
971
+ "deletedAt"
972
+ ]);
973
+ var keepLiteral = (key) => KEEP_LITERAL.has(key);
974
+ var WEB_SKELETON = {
975
+ version: 4,
976
+ flows: {
977
+ default: {
978
+ config: { platform: "web", bundle: { packages: {} } },
979
+ sources: {},
980
+ destinations: {}
981
+ }
982
+ }
983
+ };
984
+ var SERVER_SKELETON = {
985
+ version: 4,
986
+ flows: {
987
+ default: {
988
+ config: { platform: "server", bundle: { packages: {} } },
989
+ sources: {},
990
+ destinations: {}
991
+ }
992
+ }
993
+ };
994
+ var TITLE6 = "Load or Create Flow";
995
+ var DESCRIPTION6 = "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.";
996
+ var inputSchema6 = {
997
+ source: z6.string().optional().describe(
998
+ "Flow source: local file path (./flow.json), URL (https://...), inline JSON string, or API flow ID (cfg_...). Omit to create a new flow."
999
+ ),
1000
+ platform: z6.enum(["web", "server"]).optional().describe(
1001
+ "Platform for new flows. Required when source is omitted. web = browser tracking, server = Node.js HTTP."
1002
+ )
1003
+ };
1004
+ var outputSchema = {
1005
+ version: z6.number().describe("Flow config version"),
1006
+ flows: z6.record(z6.string(), z6.unknown()).describe("Flow definitions")
1007
+ };
1008
+ var annotations6 = {
1009
+ readOnlyHint: true,
1010
+ destructiveHint: false,
1011
+ idempotentHint: true,
1012
+ openWorldHint: true
1013
+ };
1014
+ function createFlowLoadToolSpec() {
1015
+ return {
1016
+ name: "flow_load",
1017
+ title: TITLE6,
1018
+ description: DESCRIPTION6,
1019
+ inputSchema: inputSchema6,
1020
+ annotations: annotations6,
1021
+ handler: (input) => flowLoadHandlerBody(input)
1022
+ };
1023
+ }
1024
+ async function flowLoadHandlerBody(input) {
1025
+ const { source, platform } = input ?? {};
1026
+ try {
1027
+ if (source) {
1028
+ const config = await loadJsonConfig2(source);
1029
+ return mcpResult7(redactNestedStrings(config, { skip: keepLiteral }), {
1030
+ next: ["Use flow_validate to check", "Use add-step prompt to modify"]
1031
+ });
1032
+ }
1033
+ if (!platform) {
1034
+ return mcpError7(
1035
+ new Error(
1036
+ "Provide source (file path, URL, or flow ID) to load existing flow, or platform (web/server) to create a new one."
1037
+ )
1038
+ );
1039
+ }
1040
+ const skeleton = platform === "web" ? WEB_SKELETON : SERVER_SKELETON;
1041
+ return mcpResult7(skeleton, {
1042
+ next: [
1043
+ "Read walkeros://reference/flow-schema for config structure",
1044
+ "Use add-step prompt to add sources and destinations"
1045
+ ]
1046
+ });
1047
+ } catch (error) {
1048
+ const msg = error instanceof Error ? error.message : "";
1049
+ if (msg.includes("not found") || msg.includes("ENOENT"))
1050
+ return mcpError7(error, "Check configPath \u2014 expected a flow.json file");
1051
+ return mcpError7(error);
1052
+ }
1053
+ }
1054
+ function registerFlowLoadTool(server) {
1055
+ const spec = createFlowLoadToolSpec();
1056
+ server.registerTool(
1057
+ spec.name,
1058
+ {
1059
+ title: spec.title,
1060
+ description: spec.description,
1061
+ inputSchema: spec.inputSchema,
1062
+ // outputSchema is wire-only; not part of ToolSpec
1063
+ outputSchema,
1064
+ annotations: spec.annotations
1065
+ },
1066
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1067
+ // type-erased (input: unknown) => Promise<unknown> form by design.
1068
+ spec.handler
1069
+ );
1070
+ }
1071
+
1072
+ // src/tools/feedback.ts
1073
+ import { z as z7 } from "zod";
1074
+ import { mcpResult as mcpResult8, mcpError as mcpError8 } from "@walkeros/core";
1075
+ var TITLE7 = "Send Feedback";
1076
+ var DESCRIPTION7 = "Send feedback about walkerOS";
1077
+ var inputSchema7 = {
1078
+ text: z7.string().describe("Your feedback text"),
1079
+ anonymous: z7.boolean().optional().describe(
1080
+ "Include user/project info? false = include, true = anonymous. Only needed on first call if not yet configured."
1081
+ )
1082
+ };
1083
+ var annotations7 = {
1084
+ readOnlyHint: false,
1085
+ destructiveHint: false,
1086
+ idempotentHint: false,
1087
+ openWorldHint: true
1088
+ };
1089
+ function createFeedbackToolSpec(client) {
1090
+ return {
1091
+ name: "feedback",
1092
+ title: TITLE7,
1093
+ description: DESCRIPTION7,
1094
+ inputSchema: inputSchema7,
1095
+ annotations: annotations7,
1096
+ handler: (input) => feedbackHandlerBody(client, input)
1097
+ };
1098
+ }
1099
+ async function feedbackHandlerBody(client, input) {
1100
+ const { text, anonymous: explicitAnonymous } = input ?? {};
1101
+ try {
1102
+ let anonymous = client.getFeedbackPreference();
1103
+ if (anonymous === void 0 && explicitAnonymous === void 0) {
1104
+ return mcpResult8(
1105
+ { needsConsent: true },
1106
+ {
1107
+ next: [
1108
+ "Ask the user if they want to include their info",
1109
+ "Call feedback again with anonymous: true or false"
1110
+ ]
1111
+ }
1112
+ );
1113
+ }
1114
+ if (anonymous === void 0 && explicitAnonymous !== void 0) {
1115
+ anonymous = explicitAnonymous;
1116
+ client.setFeedbackPreference(anonymous);
1117
+ }
1118
+ const isAnonymous = explicitAnonymous ?? anonymous ?? true;
1119
+ await client.submitFeedback(text, {
1120
+ anonymous: isAnonymous,
1121
+ version: "4.0.0-next-1777463920154"
1122
+ });
1123
+ return mcpResult8({ ok: true });
1124
+ } catch (error) {
1125
+ return mcpError8(error);
1126
+ }
1127
+ }
1128
+ function registerFeedbackTool(server, client) {
1129
+ const spec = createFeedbackToolSpec(client);
1130
+ server.registerTool(
1131
+ spec.name,
1132
+ {
1133
+ title: spec.title,
1134
+ description: spec.description,
1135
+ inputSchema: spec.inputSchema,
1136
+ annotations: spec.annotations
1137
+ },
1138
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1139
+ // type-erased (input: unknown) => Promise<unknown> form by design.
1140
+ spec.handler
1141
+ );
1142
+ }
1143
+
1144
+ // src/tools/auth.ts
1145
+ import { z as z8 } from "zod";
1146
+ import { mcpResult as mcpResult9, mcpError as mcpError9 } from "@walkeros/core";
1147
+ var TITLE8 = "Authentication";
1148
+ var DESCRIPTION8 = "Manage walkerOS authentication. Check login status, log in via device code flow, or log out. No terminal or browser required, the MCP client handles the authorization URL.";
1149
+ var inputSchema8 = {
1150
+ action: z8.enum(["status", "login", "logout"]).describe("Authentication action to perform"),
1151
+ deviceCode: z8.string().optional().describe(
1152
+ "Device code from a previous pending login attempt. Provide to resume polling without requesting a new code."
1153
+ )
1154
+ };
1155
+ var annotations8 = {
1156
+ readOnlyHint: false,
1157
+ destructiveHint: true,
1158
+ idempotentHint: false,
1159
+ openWorldHint: true
1160
+ };
1161
+ function createAuthToolSpec(client) {
1162
+ return {
1163
+ name: "auth",
1164
+ title: TITLE8,
1165
+ description: DESCRIPTION8,
1166
+ inputSchema: inputSchema8,
1167
+ annotations: annotations8,
1168
+ handler: (input) => authHandlerBody(client, input)
1169
+ };
1170
+ }
1171
+ async function authHandlerBody(client, input) {
1172
+ const { action, deviceCode } = input ?? {};
1173
+ try {
1174
+ switch (action) {
1175
+ case "status": {
1176
+ const resolved = client.resolveToken();
1177
+ if (!resolved) {
1178
+ return mcpResult9(
1179
+ { authenticated: false },
1180
+ { next: ['Use auth with action "login" to authenticate'] }
1181
+ );
1182
+ }
1183
+ const user = await client.whoami();
1184
+ return mcpResult9({
1185
+ authenticated: true,
1186
+ ...user
1187
+ });
1188
+ }
1189
+ case "login": {
1190
+ if (deviceCode) {
1191
+ const pollResult = await client.pollForToken(deviceCode, {
1192
+ timeoutMs: 6e4
1193
+ });
1194
+ if (pollResult.success) {
1195
+ return mcpResult9(
1196
+ { authenticated: true, email: pollResult.email },
1197
+ {
1198
+ next: [
1199
+ 'Use project_manage with action "list" to see your projects'
1200
+ ]
1201
+ }
1202
+ );
1203
+ }
1204
+ if (pollResult.status === "pending") {
1205
+ return mcpResult9({
1206
+ authenticated: false,
1207
+ status: "pending",
1208
+ message: "Still waiting for authorization. Try again shortly.",
1209
+ deviceCode
1210
+ });
1211
+ }
1212
+ return mcpError9(
1213
+ new Error(pollResult.error || "Authorization failed")
1214
+ );
1215
+ }
1216
+ const code = await client.requestDeviceCode();
1217
+ const loginUrl = code.verificationUriComplete || code.verificationUri;
1218
+ return mcpResult9({
1219
+ authenticated: false,
1220
+ status: "awaiting_authorization",
1221
+ loginUrl,
1222
+ message: `Open this link to authorize: ${loginUrl}`,
1223
+ deviceCode: code.deviceCode
1224
+ });
1225
+ }
1226
+ case "logout": {
1227
+ const deleted = client.deleteConfig();
1228
+ const hadEnvToken = typeof process.env.WALKEROS_TOKEN === "string" && process.env.WALKEROS_TOKEN.length > 0;
1229
+ delete process.env.WALKEROS_TOKEN;
1230
+ let message;
1231
+ if (deleted && hadEnvToken) {
1232
+ message = "Logged out. Config removed and WALKEROS_TOKEN cleared from process environment.";
1233
+ } else if (deleted) {
1234
+ message = "Logged out and config removed.";
1235
+ } else if (hadEnvToken) {
1236
+ message = "No config found. WALKEROS_TOKEN cleared from process environment.";
1237
+ } else {
1238
+ message = "No config found, already logged out.";
1239
+ }
1240
+ return mcpResult9({
1241
+ loggedOut: true,
1242
+ message
1243
+ });
1244
+ }
1245
+ default:
1246
+ throw new Error(
1247
+ `Unknown action: ${action}. Use one of: status, login, logout`
1248
+ );
1249
+ }
1250
+ } catch (error) {
1251
+ return mcpError9(error);
1252
+ }
1253
+ }
1254
+ function registerAuthTool(server, client) {
1255
+ const spec = createAuthToolSpec(client);
1256
+ server.registerTool(
1257
+ spec.name,
1258
+ {
1259
+ title: spec.title,
1260
+ description: spec.description,
1261
+ inputSchema: spec.inputSchema,
1262
+ annotations: spec.annotations
1263
+ },
1264
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1265
+ // type-erased (input: unknown) => Promise<unknown> form by design.
1266
+ spec.handler
1267
+ );
1268
+ }
1269
+
1270
+ // src/tools/project-manage.ts
1271
+ import { z as z9 } from "zod";
1272
+ import { mcpResult as mcpResult10, mcpError as mcpError10 } from "@walkeros/core";
1273
+
1274
+ // src/types.ts
1275
+ function isAuthError(error) {
1276
+ if (!(error instanceof Error)) return false;
1277
+ const msg = error.message.toLowerCase();
1278
+ if (msg.includes("unauthorized") || msg.includes("forbidden") || msg.includes("invalid token") || msg.includes("token expired") || msg.includes("not authenticated")) {
1279
+ return true;
1280
+ }
1281
+ const code = error.code;
1282
+ if (!code) return false;
1283
+ const upperCode = code.toUpperCase();
1284
+ return upperCode === "UNAUTHORIZED" || upperCode === "FORBIDDEN" || upperCode === "401" || upperCode === "403" || upperCode.startsWith("AUTH_");
1285
+ }
1286
+ var AUTH_HINT = 'Are you logged in? Use auth(action: "status") to check.';
1287
+
1288
+ // src/tools/project-manage.ts
1289
+ function wrapProjectName(p) {
1290
+ return p.name !== void 0 ? { ...p, name: wrapUserData(p.name) } : p;
1291
+ }
1292
+ var TITLE9 = "Project Management";
1293
+ var DESCRIPTION9 = "Manage walkerOS projects. List, create, update, delete projects, or set a default project for CLI operations.";
1294
+ var inputSchema9 = {
1295
+ action: z9.enum(["list", "get", "create", "update", "delete", "set_default"]).describe("Project management action to perform"),
1296
+ projectId: z9.string().optional().describe(
1297
+ "Project ID. Required for get, update, delete, and set_default actions."
1298
+ ),
1299
+ name: z9.string().optional().describe(
1300
+ "Project name. Required for create. Optional for update (to rename)."
1301
+ )
1302
+ };
1303
+ var annotations9 = {
1304
+ readOnlyHint: false,
1305
+ destructiveHint: true,
1306
+ idempotentHint: false,
1307
+ openWorldHint: true
1308
+ };
1309
+ function createProjectManageToolSpec(client) {
1310
+ return {
1311
+ name: "project_manage",
1312
+ title: TITLE9,
1313
+ description: DESCRIPTION9,
1314
+ inputSchema: inputSchema9,
1315
+ annotations: annotations9,
1316
+ handler: (input) => projectManageHandlerBody(client, input)
1317
+ };
1318
+ }
1319
+ async function projectManageHandlerBody(client, input) {
1320
+ const { action, projectId, name } = input ?? {};
1321
+ try {
1322
+ switch (action) {
1323
+ case "list": {
1324
+ const projects = await client.listProjects();
1325
+ const items = Array.isArray(projects) ? projects : projects?.projects || [];
1326
+ if (items.length === 0) {
1327
+ return mcpResult10(
1328
+ { projects: [] },
1329
+ {
1330
+ next: [
1331
+ 'Use project_manage with action "create" to create your first project',
1332
+ 'Use project_manage with action "set_default" after creating to set it as default'
1333
+ ]
1334
+ }
1335
+ );
1336
+ }
1337
+ const typedItems = items;
1338
+ const safe = Array.isArray(projects) ? typedItems.map(wrapProjectName) : { ...projects, projects: typedItems.map(wrapProjectName) };
1339
+ return mcpResult10(safe);
1340
+ }
1341
+ case "get": {
1342
+ if (!projectId) {
1343
+ return mcpError10(
1344
+ new Error(
1345
+ 'projectId is required for get action. Use action "list" to see available projects.'
1346
+ )
1347
+ );
1348
+ }
1349
+ const project = await client.getProject({ projectId });
1350
+ return mcpResult10(wrapProjectName(project));
1351
+ }
1352
+ case "create": {
1353
+ if (!name) {
1354
+ return mcpError10(new Error("name is required for create action."));
1355
+ }
1356
+ const created = await client.createProject({ name });
1357
+ return mcpResult10(wrapProjectName(created), {
1358
+ next: [
1359
+ 'Use project_manage with action "set_default" to make this your active project'
1360
+ ]
1361
+ });
1362
+ }
1363
+ case "update": {
1364
+ if (!projectId) {
1365
+ return mcpError10(
1366
+ new Error(
1367
+ 'projectId is required for update action. Use action "list" to see available projects.'
1368
+ )
1369
+ );
1370
+ }
1371
+ if (!name) {
1372
+ return mcpError10(new Error("name is required for update action."));
1373
+ }
1374
+ const updated = await client.updateProject({ projectId, name });
1375
+ return mcpResult10(wrapProjectName(updated));
1376
+ }
1377
+ case "delete": {
1378
+ if (!projectId) {
1379
+ return mcpError10(
1380
+ new Error(
1381
+ 'projectId is required for delete action. Use action "list" to see available projects.'
1382
+ )
1383
+ );
1384
+ }
1385
+ const deleted = await client.deleteProject({ projectId });
1386
+ return mcpResult10(deleted);
1387
+ }
1388
+ case "set_default": {
1389
+ if (!projectId) {
1390
+ return mcpError10(
1391
+ new Error(
1392
+ 'projectId is required for set_default action. Use action "list" to see available projects.'
1393
+ )
1394
+ );
1395
+ }
1396
+ client.setDefaultProject(projectId);
1397
+ return mcpResult10(
1398
+ { defaultProjectId: projectId },
1399
+ {
1400
+ next: [
1401
+ 'Use flow_manage with action "list" to see flows in this project'
1402
+ ]
1403
+ }
1404
+ );
1405
+ }
1406
+ default:
1407
+ throw new Error(
1408
+ `Unknown action: ${action}. Use one of: list, get, create, update, delete, set_default`
1409
+ );
1410
+ }
1411
+ } catch (error) {
1412
+ return mcpError10(error, isAuthError(error) ? AUTH_HINT : void 0);
1413
+ }
1414
+ }
1415
+ function registerProjectManageTool(server, client) {
1416
+ const spec = createProjectManageToolSpec(client);
1417
+ server.registerTool(
1418
+ spec.name,
1419
+ {
1420
+ title: spec.title,
1421
+ description: spec.description,
1422
+ inputSchema: spec.inputSchema,
1423
+ annotations: spec.annotations
1424
+ },
1425
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1426
+ // type-erased (input: unknown) => Promise<unknown> form by design.
1427
+ spec.handler
1428
+ );
1429
+ }
1430
+
1431
+ // src/tools/flow-manage.ts
1432
+ import { z as z10 } from "zod";
1433
+ import { mcpResult as mcpResult11, mcpError as mcpError11 } from "@walkeros/core";
1434
+
1435
+ // src/ui-parts.ts
1436
+ function flowCanvasResult(payload) {
1437
+ const structured = { kind: "flow-canvas", ...payload };
1438
+ return {
1439
+ content: [
1440
+ {
1441
+ type: "text",
1442
+ text: JSON.stringify(structured, null, 2)
1443
+ }
1444
+ ],
1445
+ structuredContent: structured
1446
+ };
1447
+ }
1448
+
1449
+ // src/tools/flow-manage.ts
1450
+ function pickPlatform(content) {
1451
+ if (content && typeof content === "object") {
1452
+ const flows = content.flows;
1453
+ if (flows && typeof flows === "object") {
1454
+ for (const flow of Object.values(flows)) {
1455
+ if (flow && typeof flow === "object") {
1456
+ const config = flow.config;
1457
+ if (config && typeof config === "object") {
1458
+ const platform = config.platform;
1459
+ if (platform === "web" || platform === "server") return platform;
1460
+ }
1461
+ }
1462
+ }
1463
+ }
1464
+ }
1465
+ return "server";
1466
+ }
1467
+ var KEEP_LITERAL2 = /* @__PURE__ */ new Set([
1468
+ "id",
1469
+ "flowId",
1470
+ "projectId",
1471
+ "previewId",
1472
+ "version",
1473
+ "slug",
1474
+ "createdAt",
1475
+ "updatedAt",
1476
+ "deletedAt"
1477
+ ]);
1478
+ var keepLiteral2 = (key) => KEEP_LITERAL2.has(key);
1479
+ function safeSummary(flow) {
1480
+ return flow.name !== void 0 ? { ...flow, name: wrapUserData(flow.name) } : flow;
1481
+ }
1482
+ function safeDetail(flow) {
1483
+ const withName = flow.name !== void 0 ? { ...flow, name: wrapUserData(flow.name) } : { ...flow };
1484
+ if (flow.config !== void 0) {
1485
+ withName.config = redactNestedStrings(
1486
+ flow.config,
1487
+ { skip: keepLiteral2 }
1488
+ );
1489
+ }
1490
+ return withName;
1491
+ }
1492
+ var TITLE10 = "Flow Management";
1493
+ var DESCRIPTION10 = "Manage walkerOS flows and their previews. List/get/create/update/delete/duplicate flows, or create/inspect/delete preview bundles for testing flow changes on live sites.";
1494
+ var inputSchema10 = {
1495
+ action: z10.enum([
1496
+ "list",
1497
+ "get",
1498
+ "create",
1499
+ "update",
1500
+ "delete",
1501
+ "duplicate",
1502
+ "preview_list",
1503
+ "preview_get",
1504
+ "preview_create",
1505
+ "preview_delete"
1506
+ ]).describe("Flow management action to perform"),
1507
+ flowId: z10.string().optional().describe(
1508
+ "Flow ID (flow_...) or config ID (cfg_...). Required for get, update, delete, duplicate."
1509
+ ),
1510
+ projectId: z10.string().optional().describe(
1511
+ "Project ID. Optional filter for list (omit to list all projects). Required for create if no default project set."
1512
+ ),
1513
+ name: z10.string().optional().describe(
1514
+ "Flow name. Required for create. Optional for update (to rename) and duplicate."
1515
+ ),
1516
+ content: z10.record(z10.string(), z10.unknown()).optional().describe("Flow.Json content. Used for create and update."),
1517
+ patch: z10.boolean().optional().describe(
1518
+ "Merge-patch for update (default true). When true, only provided fields are updated."
1519
+ ),
1520
+ fields: z10.array(z10.string()).optional().describe("Dot-path selectors for get to return only specific fields."),
1521
+ sort: z10.enum(["name", "updated_at", "created_at"]).optional().describe("Sort field for list."),
1522
+ order: z10.enum(["asc", "desc"]).optional().describe("Sort order for list."),
1523
+ includeDeleted: z10.boolean().optional().describe("Include soft-deleted flows in list results."),
1524
+ previewId: z10.string().optional().describe(
1525
+ "Preview ID (prv_...). Required for preview_get and preview_delete."
1526
+ ),
1527
+ flowName: z10.string().optional().describe(
1528
+ "Flow settings name. Used by preview_create as an alternative to flowSettingsId."
1529
+ ),
1530
+ flowSettingsId: z10.string().optional().describe(
1531
+ "Flow settings ID. Used by preview_create as an alternative to flowName."
1532
+ ),
1533
+ siteUrl: z10.string().optional().describe(
1534
+ "Optional site URL for preview_create. When provided, the response includes full activationUrl and deactivationUrl the user can click."
1535
+ )
1536
+ };
1537
+ var annotations10 = {
1538
+ readOnlyHint: false,
1539
+ destructiveHint: true,
1540
+ idempotentHint: false,
1541
+ openWorldHint: true
1542
+ };
1543
+ function createFlowManageToolSpec(client) {
1544
+ return {
1545
+ name: "flow_manage",
1546
+ title: TITLE10,
1547
+ description: DESCRIPTION10,
1548
+ inputSchema: inputSchema10,
1549
+ annotations: annotations10,
1550
+ handler: (input) => flowManageHandlerBody(client, input)
1551
+ };
1552
+ }
1553
+ async function flowManageHandlerBody(client, input) {
1554
+ const {
1555
+ action,
1556
+ flowId,
1557
+ projectId,
1558
+ name,
1559
+ content,
1560
+ patch,
1561
+ fields,
1562
+ sort,
1563
+ order,
1564
+ includeDeleted,
1565
+ previewId,
1566
+ flowName,
1567
+ flowSettingsId,
1568
+ siteUrl
1569
+ } = input ?? {};
1570
+ try {
1571
+ switch (action) {
1572
+ case "list": {
1573
+ if (projectId) {
1574
+ const data2 = await client.listFlows({
1575
+ projectId,
1576
+ sort,
1577
+ order,
1578
+ includeDeleted
1579
+ });
1580
+ const dataObj = data2;
1581
+ const flows = dataObj.flows;
1582
+ const safe2 = Array.isArray(flows) ? { ...dataObj, flows: flows.map(safeSummary) } : data2;
1583
+ return mcpResult11(safe2);
1584
+ }
1585
+ const data = await client.listAllFlows({
1586
+ sort,
1587
+ order,
1588
+ includeDeleted
1589
+ });
1590
+ const safe = Array.isArray(data) ? data.map(safeSummary) : data;
1591
+ return mcpResult11(
1592
+ { projects: safe },
1593
+ {
1594
+ next: [
1595
+ 'Use flow_manage with action "get" and a flowId to inspect a specific flow',
1596
+ "Use flow_load to open a flow for editing"
1597
+ ]
1598
+ }
1599
+ );
1600
+ }
1601
+ case "get": {
1602
+ if (!flowId) {
1603
+ return mcpError11(
1604
+ new Error(
1605
+ 'flowId is required for get action. Use action "list" to see available flows.'
1606
+ )
1607
+ );
1608
+ }
1609
+ const flow = await client.getFlow({ flowId, projectId, fields });
1610
+ const safe = safeDetail(
1611
+ flow
1612
+ );
1613
+ return flowCanvasResult({
1614
+ flowId: safe.id,
1615
+ configName: safe.name ?? "default",
1616
+ platform: pickPlatform(safe.config),
1617
+ flowConfig: safe.config ?? {},
1618
+ suggestions: [
1619
+ {
1620
+ label: "Validate this flow",
1621
+ prompt: `Validate flow ${safe.id}`,
1622
+ autoSend: true
1623
+ },
1624
+ {
1625
+ label: "Add a destination",
1626
+ prompt: `Help me add a destination to flow ${safe.id}`
1627
+ }
1628
+ ]
1629
+ });
1630
+ }
1631
+ case "create": {
1632
+ if (!name) {
1633
+ return mcpError11(new Error("name is required for create action."));
1634
+ }
1635
+ const created = await client.createFlow({
1636
+ name,
1637
+ content: content ?? {},
1638
+ projectId
1639
+ });
1640
+ const safeCreated = safeDetail(
1641
+ created
1642
+ );
1643
+ return flowCanvasResult({
1644
+ flowId: safeCreated.id,
1645
+ configName: safeCreated.name ?? "default",
1646
+ platform: pickPlatform(safeCreated.config),
1647
+ flowConfig: safeCreated.config ?? {},
1648
+ suggestions: [
1649
+ {
1650
+ label: "Validate this flow",
1651
+ prompt: `Validate flow ${safeCreated.id}`,
1652
+ autoSend: true
1653
+ },
1654
+ {
1655
+ label: "Deploy it",
1656
+ prompt: `Deploy flow ${safeCreated.id}`
1657
+ }
1658
+ ]
1659
+ });
1660
+ }
1661
+ case "update": {
1662
+ if (!flowId) {
1663
+ return mcpError11(
1664
+ new Error(
1665
+ 'flowId is required for update action. Use action "list" to see available flows.'
1666
+ )
1667
+ );
1668
+ }
1669
+ const updated = await client.updateFlow({
1670
+ flowId,
1671
+ projectId,
1672
+ name,
1673
+ content,
1674
+ mergePatch: patch ?? true
1675
+ });
1676
+ const safeUpdated = safeDetail(
1677
+ updated
1678
+ );
1679
+ return flowCanvasResult({
1680
+ flowId: safeUpdated.id,
1681
+ configName: safeUpdated.name ?? "default",
1682
+ platform: pickPlatform(safeUpdated.config),
1683
+ flowConfig: safeUpdated.config ?? {},
1684
+ suggestions: [
1685
+ {
1686
+ label: "Validate this flow",
1687
+ prompt: `Validate flow ${safeUpdated.id}`,
1688
+ autoSend: true
1689
+ },
1690
+ {
1691
+ label: "Deploy it",
1692
+ prompt: `Deploy flow ${safeUpdated.id}`
1693
+ }
1694
+ ]
1695
+ });
1696
+ }
1697
+ case "delete": {
1698
+ if (!flowId) {
1699
+ return mcpError11(
1700
+ new Error(
1701
+ 'flowId is required for delete action. Use action "list" to see available flows.'
1702
+ )
1703
+ );
1704
+ }
1705
+ const deleted = await client.deleteFlow({ flowId, projectId });
1706
+ return mcpResult11(deleted);
1707
+ }
1708
+ case "duplicate": {
1709
+ if (!flowId) {
1710
+ return mcpError11(
1711
+ new Error(
1712
+ 'flowId is required for duplicate action. Use action "list" to see available flows.'
1713
+ )
1714
+ );
1715
+ }
1716
+ const duplicated = await client.duplicateFlow({
1717
+ flowId,
1718
+ name,
1719
+ projectId
1720
+ });
1721
+ return mcpResult11(
1722
+ safeDetail(duplicated)
1723
+ );
1724
+ }
1725
+ case "preview_list": {
1726
+ if (!flowId) {
1727
+ return mcpError11(
1728
+ new Error(
1729
+ 'flowId is required for preview_list. Use action "list" to see available flows.'
1730
+ )
1731
+ );
1732
+ }
1733
+ const data = await client.listPreviews({ projectId, flowId });
1734
+ return mcpResult11(data);
1735
+ }
1736
+ case "preview_get": {
1737
+ if (!flowId || !previewId) {
1738
+ return mcpError11(
1739
+ new Error("flowId and previewId are required for preview_get.")
1740
+ );
1741
+ }
1742
+ const data = await client.getPreview({
1743
+ projectId,
1744
+ flowId,
1745
+ previewId
1746
+ });
1747
+ return mcpResult11(data);
1748
+ }
1749
+ case "preview_create": {
1750
+ if (!flowId) {
1751
+ return mcpError11(new Error("flowId is required for preview_create."));
1752
+ }
1753
+ if (!flowName && !flowSettingsId) {
1754
+ return mcpError11(
1755
+ new Error(
1756
+ "flowName or flowSettingsId is required for preview_create."
1757
+ )
1758
+ );
1759
+ }
1760
+ const preview = await client.createPreview({
1761
+ projectId,
1762
+ flowId,
1763
+ flowName,
1764
+ flowSettingsId
1765
+ });
1766
+ const typedPreview = preview;
1767
+ const enriched = {
1768
+ ...typedPreview,
1769
+ activationParam: typedPreview.activationUrl
1770
+ };
1771
+ if (siteUrl) {
1772
+ const on = new URL(siteUrl);
1773
+ on.searchParams.set("elbPreview", typedPreview.token);
1774
+ enriched.activationUrl = on.toString();
1775
+ const off = new URL(siteUrl);
1776
+ off.searchParams.set("elbPreview", "off");
1777
+ enriched.deactivationUrl = off.toString();
1778
+ } else {
1779
+ delete enriched.activationUrl;
1780
+ }
1781
+ return mcpResult11(enriched, {
1782
+ next: siteUrl ? [
1783
+ "Open activationUrl to activate preview mode; open deactivationUrl to exit."
1784
+ ] : [
1785
+ "Append activationParam to any URL on your site to activate preview mode."
1786
+ ]
1787
+ });
1788
+ }
1789
+ case "preview_delete": {
1790
+ if (!flowId || !previewId) {
1791
+ return mcpError11(
1792
+ new Error("flowId and previewId are required for preview_delete.")
1793
+ );
1794
+ }
1795
+ const data = await client.deletePreview({
1796
+ projectId,
1797
+ flowId,
1798
+ previewId
1799
+ });
1800
+ return mcpResult11(data);
1801
+ }
1802
+ default:
1803
+ throw new Error(
1804
+ `Unknown action: ${action}. Use one of: list, get, create, update, delete, duplicate, preview_list, preview_get, preview_create, preview_delete`
1805
+ );
1806
+ }
1807
+ } catch (error) {
1808
+ return mcpError11(error, isAuthError(error) ? AUTH_HINT : void 0);
1809
+ }
1810
+ }
1811
+ function registerFlowManageTool(server, client) {
1812
+ const spec = createFlowManageToolSpec(client);
1813
+ server.registerTool(
1814
+ spec.name,
1815
+ {
1816
+ title: spec.title,
1817
+ description: spec.description,
1818
+ inputSchema: spec.inputSchema,
1819
+ annotations: spec.annotations
1820
+ },
1821
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
1822
+ // type-erased (input: unknown) => Promise<unknown> form by design.
1823
+ spec.handler
1824
+ );
1825
+ }
1826
+
1827
+ // src/tools/deploy-manage.ts
1828
+ import { z as z11 } from "zod";
1829
+ import { mcpResult as mcpResult12, mcpError as mcpError12 } from "@walkeros/core";
1830
+
1831
+ // src/tools/_resolvers.ts
1832
+ var DeploymentNotFoundError = class extends Error {
1833
+ code = "NOT_FOUND";
1834
+ constructor(message) {
1835
+ super(message);
1836
+ this.name = "DeploymentNotFoundError";
1837
+ }
1838
+ };
1839
+ var MultipleDeploymentsError = class extends Error {
1840
+ code = "MULTIPLE_DEPLOYMENTS";
1841
+ details;
1842
+ constructor(message, details) {
1843
+ super(message);
1844
+ this.name = "MultipleDeploymentsError";
1845
+ this.details = details;
1846
+ }
1847
+ };
1848
+ async function resolveDeploymentSlug(args) {
1849
+ const matches = await args.list({
1850
+ projectId: args.projectId,
1851
+ flowId: args.flowId
1852
+ });
1853
+ if (matches.length === 0) {
1854
+ throw new DeploymentNotFoundError(
1855
+ `No deployments found for flow ${args.flowId}`
1856
+ );
1857
+ }
1858
+ if (args.slug !== void 0) {
1859
+ const hit = matches.find((m) => m.slug === args.slug);
1860
+ if (!hit) {
1861
+ throw new DeploymentNotFoundError(
1862
+ `No deployment with slug ${args.slug} in flow ${args.flowId}`
1863
+ );
1864
+ }
1865
+ return hit.slug;
1866
+ }
1867
+ if (matches.length === 1) {
1868
+ return matches[0].slug;
1869
+ }
1870
+ throw new MultipleDeploymentsError(
1871
+ `Flow ${args.flowId} has ${matches.length} active deployments; pass slug to disambiguate`,
1872
+ matches.map((m) => ({
1873
+ slug: m.slug,
1874
+ type: m.type,
1875
+ status: m.status,
1876
+ updatedAt: m.updatedAt
1877
+ }))
1878
+ );
1879
+ }
1880
+
1881
+ // src/tools/deploy-manage.ts
1882
+ var TITLE11 = "Deploy Management";
1883
+ var DESCRIPTION11 = "Deploy walkerOS flows and manage deployments. For get/delete actions 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.";
1884
+ var inputSchema11 = {
1885
+ action: z11.enum(["deploy", "list", "get", "delete"]).describe("Deployment action to perform"),
1886
+ projectId: z11.string().optional().describe("Project ID. Optional; falls back to the default project."),
1887
+ flowId: z11.string().optional().describe(
1888
+ "Flow ID. Required for: deploy, get, delete. Optional filter for list."
1889
+ ),
1890
+ slug: z11.string().optional().describe(
1891
+ "Deployment slug. Optional disambiguator for get/delete when the flow has multiple active deployments."
1892
+ ),
1893
+ type: z11.enum(["web", "server"]).optional().describe("Deployment type filter for list."),
1894
+ status: z11.string().optional().describe("Status filter for list."),
1895
+ wait: z11.boolean().optional().describe(
1896
+ "Wait for deploy to complete (default true). Only used with deploy action."
1897
+ ),
1898
+ flowName: z11.string().optional().describe(
1899
+ "Flow name for multi-settings flows. Only used with deploy action."
1900
+ )
1901
+ };
1902
+ var annotations11 = {
1903
+ readOnlyHint: false,
1904
+ destructiveHint: true,
1905
+ idempotentHint: false,
1906
+ openWorldHint: true
1907
+ };
1908
+ function listForResolver(client, projectId) {
1909
+ return async (q) => {
1910
+ const resp = await client.listDeployments({
1911
+ projectId: projectId || q.projectId || void 0,
1912
+ flowId: q.flowId
1913
+ });
1914
+ return resp.deployments ?? [];
1915
+ };
1916
+ }
1917
+ function createDeployManageToolSpec(client) {
1918
+ return {
1919
+ name: "deploy_manage",
1920
+ title: TITLE11,
1921
+ description: DESCRIPTION11,
1922
+ inputSchema: inputSchema11,
1923
+ annotations: annotations11,
1924
+ handler: (input) => deployManageHandlerBody(client, input)
1925
+ };
1926
+ }
1927
+ async function deployManageHandlerBody(client, input) {
1928
+ const { action, projectId, flowId, slug, type, status, wait, flowName } = input ?? {};
1929
+ try {
1930
+ switch (action) {
1931
+ case "deploy": {
1932
+ if (!flowId) {
1933
+ return mcpError12(
1934
+ new Error(
1935
+ 'flowId is required for deploy action. Use flow_manage with action "list" to see available flows.'
1936
+ )
1937
+ );
1938
+ }
1939
+ const result = await client.deploy({
1940
+ flowId,
1941
+ wait: wait ?? true,
1942
+ flowName
1943
+ });
1944
+ return mcpResult12(result, {
1945
+ next: [
1946
+ 'Use deploy_manage with action "get" to check deployment status'
1947
+ ]
1948
+ });
1949
+ }
1950
+ case "list": {
1951
+ const data = await client.listDeployments({
1952
+ projectId,
1953
+ flowId,
1954
+ type,
1955
+ status
1956
+ });
1957
+ return mcpResult12(data);
1958
+ }
1959
+ case "get": {
1960
+ if (!flowId) {
1961
+ return mcpError12(
1962
+ new Error(
1963
+ 'flowId is required for get action. Use flow_manage with action "list" to see available flows.'
1964
+ )
1965
+ );
1966
+ }
1967
+ const resolvedSlug = await resolveDeploymentSlug({
1968
+ projectId: projectId ?? "",
1969
+ flowId,
1970
+ slug,
1971
+ list: listForResolver(client, projectId)
1972
+ });
1973
+ const data = await client.getDeploymentBySlug({
1974
+ slug: resolvedSlug,
1975
+ projectId
1976
+ });
1977
+ return mcpResult12(data);
1978
+ }
1979
+ case "delete": {
1980
+ if (!flowId) {
1981
+ return mcpError12(
1982
+ new Error(
1983
+ 'flowId is required for delete action. Use flow_manage with action "list" to see available flows.'
1984
+ )
1985
+ );
1986
+ }
1987
+ const resolvedSlug = await resolveDeploymentSlug({
1988
+ projectId: projectId ?? "",
1989
+ flowId,
1990
+ slug,
1991
+ list: listForResolver(client, projectId)
1992
+ });
1993
+ const data = await client.deleteDeployment({
1994
+ slug: resolvedSlug,
1995
+ projectId
1996
+ });
1997
+ return mcpResult12({
1998
+ deleted: true,
1999
+ ...data
2000
+ });
2001
+ }
2002
+ default:
2003
+ throw new Error(
2004
+ `Unknown action: ${action}. Use one of: deploy, list, get, delete`
2005
+ );
2006
+ }
2007
+ } catch (error) {
2008
+ return mcpError12(error, isAuthError(error) ? AUTH_HINT : void 0);
2009
+ }
2010
+ }
2011
+ function registerDeployTool(server, client) {
2012
+ const spec = createDeployManageToolSpec(client);
2013
+ server.registerTool(
2014
+ spec.name,
2015
+ {
2016
+ title: spec.title,
2017
+ description: spec.description,
2018
+ inputSchema: spec.inputSchema,
2019
+ annotations: spec.annotations
2020
+ },
2021
+ // SDK infers handler type from inputSchema shape; ToolSpec.handler is the
2022
+ // type-erased (input: unknown) => Promise<unknown> form by design.
2023
+ spec.handler
2024
+ );
2025
+ }
2026
+
2027
+ // src/resources/package-schemas.ts
2028
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2029
+ import { fetchPackageSchema } from "@walkeros/core";
2030
+ function registerPackageSchemaResources(server) {
2031
+ const template = new ResourceTemplate("walkeros://schema/{packageName}", {
2032
+ list: async () => {
2033
+ const catalog = await fetchCatalog();
2034
+ return {
2035
+ resources: catalog.map((pkg) => ({
2036
+ uri: `walkeros://schema/${encodeURIComponent(pkg.name)}`,
2037
+ name: pkg.name,
2038
+ description: `Schema and examples for ${pkg.name}`,
2039
+ mimeType: "application/json"
2040
+ }))
2041
+ };
2042
+ }
2043
+ });
2044
+ server.registerResource(
2045
+ "package-schema",
2046
+ template,
2047
+ {
2048
+ title: "walkerOS Package Schema",
2049
+ description: "JSON Schema and configuration examples for walkerOS packages",
2050
+ mimeType: "application/json"
2051
+ },
2052
+ async (uri, { packageName }) => {
2053
+ const info = await fetchPackageSchema(
2054
+ decodeURIComponent(packageName)
2055
+ );
2056
+ return {
2057
+ contents: [
2058
+ {
2059
+ uri: uri.toString(),
2060
+ mimeType: "application/json",
2061
+ text: JSON.stringify(info, null, 2)
2062
+ }
2063
+ ]
2064
+ };
2065
+ }
2066
+ );
2067
+ }
2068
+
2069
+ // src/resources/references.ts
2070
+ import { schemas as schemas5 } from "@walkeros/core/dev";
2071
+ function registerReferenceResources(server) {
2072
+ server.resource(
2073
+ "flow-schema",
2074
+ "walkeros://reference/flow-schema",
2075
+ {
2076
+ description: "JSON Schema for Flow.Json \u2014 the complete flow configuration structure",
2077
+ mimeType: "application/json"
2078
+ },
2079
+ async () => ({
2080
+ contents: [
2081
+ {
2082
+ uri: "walkeros://reference/flow-schema",
2083
+ text: JSON.stringify(schemas5.configJsonSchema, null, 2),
2084
+ mimeType: "application/json"
2085
+ }
2086
+ ]
2087
+ })
2088
+ );
2089
+ server.resource(
2090
+ "event-model",
2091
+ "walkeros://reference/event-model",
2092
+ {
2093
+ description: "JSON Schema for walkerOS events: entity-action naming, data, context, globals, user, consent",
2094
+ mimeType: "application/json"
2095
+ },
2096
+ async () => ({
2097
+ contents: [
2098
+ {
2099
+ uri: "walkeros://reference/event-model",
2100
+ text: JSON.stringify(schemas5.eventJsonSchema, null, 2),
2101
+ mimeType: "application/json"
2102
+ }
2103
+ ]
2104
+ })
2105
+ );
2106
+ server.resource(
2107
+ "mapping",
2108
+ "walkeros://reference/mapping",
2109
+ {
2110
+ description: "JSON Schemas for walkerOS mapping: rules, valueConfig, rule, policy",
2111
+ mimeType: "application/json"
2112
+ },
2113
+ async () => ({
2114
+ contents: [
2115
+ {
2116
+ uri: "walkeros://reference/mapping",
2117
+ text: JSON.stringify(
2118
+ {
2119
+ rules: schemas5.rulesJsonSchema,
2120
+ valueConfig: schemas5.valueConfigJsonSchema,
2121
+ rule: schemas5.ruleJsonSchema,
2122
+ policy: schemas5.policyJsonSchema
2123
+ },
2124
+ null,
2125
+ 2
2126
+ ),
2127
+ mimeType: "application/json"
2128
+ }
2129
+ ]
2130
+ })
2131
+ );
2132
+ server.resource(
2133
+ "consent",
2134
+ "walkeros://reference/consent",
2135
+ {
2136
+ description: "JSON Schema for walkerOS consent: destination-level, rule-level, and field-level consent gating",
2137
+ mimeType: "application/json"
2138
+ },
2139
+ async () => ({
2140
+ contents: [
2141
+ {
2142
+ uri: "walkeros://reference/consent",
2143
+ text: JSON.stringify(schemas5.consentJsonSchema, null, 2),
2144
+ mimeType: "application/json"
2145
+ }
2146
+ ]
2147
+ })
2148
+ );
2149
+ server.resource(
2150
+ "variables",
2151
+ "walkeros://reference/variables",
2152
+ {
2153
+ description: "walkerOS variable patterns: $var, $env, $def, $contract, $code, $store, $secret substitution",
2154
+ mimeType: "application/json"
2155
+ },
2156
+ async () => ({
2157
+ contents: [
2158
+ {
2159
+ uri: "walkeros://reference/variables",
2160
+ text: JSON.stringify(
2161
+ {
2162
+ separatorRule: "`.` for names and paths; `:` for literal values or raw-code payloads.",
2163
+ patterns: {
2164
+ "$var.name": "Variable substitution \u2014 cascade: step settings > flow settings > config variables",
2165
+ "$env.NAME": "Environment variable \u2014 $env.GA_ID reads process.env.GA_ID",
2166
+ "$env.NAME:default": "Environment variable with fallback \u2014 $env.GA_ID:G-DEFAULT (the `:` is the literal default separator)",
2167
+ "$def.name": "Definition reference \u2014 reusable config blocks from definitions section",
2168
+ "$def.name.path.deep": "Nested definition access \u2014 $def.ga4Events.purchase",
2169
+ "$contract.name": "Contract reference \u2014 links to named contract for validation",
2170
+ "$contract.name.path": "Nested contract access \u2014 $contract.ecommerce.product",
2171
+ "$code:(expr)": "Inline JavaScript \u2014 $code:(event) => event.data.price * 100 (the `:` carries the raw-code payload)",
2172
+ "$store.storeId": "Store injection in env values \u2014 wires runtime store access",
2173
+ "$secret.NAME": "Secret injection \u2014 resolved server-side at deploy/runtime"
2174
+ },
2175
+ cascade: {
2176
+ priority: [
2177
+ "1. Step-level settings (highest)",
2178
+ "2. Flow-level settings",
2179
+ "3. Config-level variables (lowest)"
2180
+ ],
2181
+ example: {
2182
+ variables: { apiKey: "default-key" },
2183
+ flows: {
2184
+ production: {
2185
+ destinations: {
2186
+ api: {
2187
+ config: { key: "$var.apiKey" },
2188
+ settings: { apiKey: "prod-key" }
2189
+ }
2190
+ }
2191
+ }
2192
+ }
2193
+ }
2194
+ }
2195
+ },
2196
+ null,
2197
+ 2
2198
+ ),
2199
+ mimeType: "application/json"
2200
+ }
2201
+ ]
2202
+ })
2203
+ );
2204
+ server.resource(
2205
+ "contract",
2206
+ "walkeros://reference/contract",
2207
+ {
2208
+ description: "JSON Schema for walkerOS contracts: event schema validation with entity-action keying",
2209
+ mimeType: "application/json"
2210
+ },
2211
+ async () => ({
2212
+ contents: [
2213
+ {
2214
+ uri: "walkeros://reference/contract",
2215
+ text: JSON.stringify(schemas5.contractJsonSchema, null, 2),
2216
+ mimeType: "application/json"
2217
+ }
2218
+ ]
2219
+ })
2220
+ );
2221
+ server.resource(
2222
+ "examples",
2223
+ "walkeros://reference/examples",
2224
+ {
2225
+ description: "Complete flow config example: web + server flows, mapping, contracts, step examples",
2226
+ mimeType: "application/json"
2227
+ },
2228
+ async () => {
2229
+ let example;
2230
+ try {
2231
+ const { readFileSync } = await import("fs");
2232
+ const { createRequire } = await import("module");
2233
+ const require2 = createRequire(import.meta.url);
2234
+ const examplePath = require2.resolve("@walkeros/cli/examples/flow-complete.json");
2235
+ example = readFileSync(examplePath, "utf-8");
2236
+ } catch {
2237
+ example = JSON.stringify({ error: "Example not found" });
2238
+ }
2239
+ return {
2240
+ contents: [
2241
+ {
2242
+ uri: "walkeros://reference/examples",
2243
+ text: example,
2244
+ mimeType: "application/json"
2245
+ }
2246
+ ]
2247
+ };
2248
+ }
2249
+ );
2250
+ server.resource(
2251
+ "openapi",
2252
+ "walkeros://reference/openapi",
2253
+ {
2254
+ description: "walkerOS cloud API \u2014 OpenAPI 3.1 specification",
2255
+ mimeType: "application/json"
2256
+ },
2257
+ async () => {
2258
+ let openApiSpec;
2259
+ try {
2260
+ const { readFileSync } = await import("fs");
2261
+ const { createRequire } = await import("module");
2262
+ const require2 = createRequire(import.meta.url);
2263
+ const specPath = require2.resolve("@walkeros/cli/openapi/spec.json");
2264
+ openApiSpec = readFileSync(specPath, "utf-8");
2265
+ } catch {
2266
+ openApiSpec = JSON.stringify({ error: "OpenAPI spec not found" });
2267
+ }
2268
+ return {
2269
+ contents: [
2270
+ {
2271
+ uri: "walkeros://reference/openapi",
2272
+ text: openApiSpec,
2273
+ mimeType: "application/json"
2274
+ }
2275
+ ]
2276
+ };
2277
+ }
2278
+ );
2279
+ server.resource(
2280
+ "packages",
2281
+ "walkeros://reference/packages",
2282
+ {
2283
+ description: "Complete walkerOS package catalog \u2014 all sources, destinations, transformers, and stores",
2284
+ mimeType: "application/json"
2285
+ },
2286
+ async () => {
2287
+ const catalog = await fetchCatalog();
2288
+ return {
2289
+ contents: [
2290
+ {
2291
+ uri: "walkeros://reference/packages",
2292
+ text: JSON.stringify(catalog, null, 2),
2293
+ mimeType: "application/json"
2294
+ }
2295
+ ]
2296
+ };
2297
+ }
2298
+ );
2299
+ }
2300
+
2301
+ // src/prompts/add-step.ts
2302
+ import { z as z12 } from "zod";
2303
+ function registerAddStepPrompt(server) {
2304
+ server.registerPrompt(
2305
+ "add-step",
2306
+ {
2307
+ description: "Add a source, destination, transformer, or store step to a flow configuration. Guides through package selection, config scaffolding, and wiring.",
2308
+ argsSchema: {
2309
+ stepType: z12.string().optional().describe(
2310
+ "Type of step to add: source, destination, transformer, or store"
2311
+ ),
2312
+ flowPath: z12.string().optional().describe("Path to the flow.json file to modify")
2313
+ }
2314
+ },
2315
+ async ({ stepType, flowPath }) => ({
2316
+ messages: [
2317
+ {
2318
+ role: "user",
2319
+ content: {
2320
+ type: "text",
2321
+ text: [
2322
+ `Help me add a ${stepType || "new"} step to my flow${flowPath ? ` at ${flowPath}` : ""}.`,
2323
+ "",
2324
+ "Follow these steps:",
2325
+ `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.`,
2326
+ `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.`,
2327
+ '3. Use package_get with section="examples" to see working configuration examples.',
2328
+ "4. Scaffold the step config using the package schemas \u2014 include required settings with placeholder values.",
2329
+ "5. Wire the step into the flow: add to packages section (with version if needed), connect via next/before chains if needed.",
2330
+ '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.',
2331
+ '7. For destinations: if consent-gated loading is needed, add require: ["consent"] to config. Note: require delays initialization until a "walker consent" event fires. When simulating with flow_simulate, destinations with require will error "not found" \u2014 remove require temporarily or test without it. For per-event consent filtering, add consent: { marketing: true } to config.',
2332
+ "8. Use flow_validate to verify the result.",
2333
+ "9. 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.",
2334
+ "10. When adding a transformer that uses ingest fields, verify the source has `ingest` configured \u2014 otherwise ingest fields resolve to empty values.",
2335
+ "",
2336
+ "Important:",
2337
+ "- Read the walkeros://reference/flow-schema resource to understand connection rules.",
2338
+ "- Sources connect to pre-collector transformers via `next`.",
2339
+ "- Destinations connect to post-collector transformers via `before`.",
2340
+ "- Stores are passive \u2014 referenced via `$store.storeName` in env values.",
2341
+ "- Use variables ($var) for values that change between environments.",
2342
+ "- For required settings without defaults in the package schema, ask the user which value to use. Do not guess credentials, IDs, or environment-specific values.",
2343
+ "- If $meta.exports lists named exports, set the `code` field on the step to the chosen export name. If only one export exists, use it automatically."
2344
+ ].join("\n")
2345
+ }
2346
+ }
2347
+ ]
2348
+ })
2349
+ );
2350
+ }
2351
+
2352
+ // src/prompts/setup-mapping.ts
2353
+ import { z as z13 } from "zod";
2354
+ function registerSetupMappingPrompt(server) {
2355
+ server.registerPrompt(
2356
+ "setup-mapping",
2357
+ {
2358
+ description: "Set up event mapping for any step in a flow. Teaches mapping syntax and uses package examples as templates.",
2359
+ argsSchema: {
2360
+ stepName: z13.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
2361
+ }
2362
+ },
2363
+ async ({ stepName }) => ({
2364
+ messages: [
2365
+ {
2366
+ role: "user",
2367
+ content: {
2368
+ type: "text",
2369
+ text: [
2370
+ `Help me set up mapping${stepName ? ` for the "${stepName}" step` : ""}.`,
2371
+ "",
2372
+ "Follow these steps:",
2373
+ "1. Read the walkeros://reference/mapping resource for syntax reference.",
2374
+ `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.`,
2375
+ "3. Ask whether this is source mapping (raw input \u2192 walkerOS events) or destination mapping (walkerOS events \u2192 vendor format).",
2376
+ "4. Ask which events to map (one at a time, not all at once).",
2377
+ "5. Generate one mapping rule using the package examples as templates. Validate it with flow_validate before moving to the next.",
2378
+ "6. Repeat for each event.",
2379
+ "",
2380
+ 'Mapping uses nested entity \u2192 action keys. Event "product add" maps to `{ "product": { "add": Rule } }`. Wildcards: `{ "*": { "view": Rule } }`.',
2381
+ "",
2382
+ "Use $def references for shared mapping patterns across destinations.",
2383
+ "",
2384
+ "Policy and consent in mapping:",
2385
+ "- config.policy runs BEFORE mapping rules \u2014 use it to inject or redact fields on the event.",
2386
+ "- rule.policy runs after config.policy but before data transformation \u2014 use for event-specific pre-processing.",
2387
+ "- config.consent gates ALL events to this destination. rule.consent gates specific event types.",
2388
+ "- Individual value configs support consent: { marketing: true } for field-level gating."
2389
+ ].join("\n")
2390
+ }
2391
+ }
2392
+ ]
2393
+ })
2394
+ );
2395
+ }
2396
+
2397
+ // src/prompts/manage-contract.ts
2398
+ import { z as z14 } from "zod";
2399
+ function registerManageContractPrompt(server) {
2400
+ server.registerPrompt(
2401
+ "manage-contract",
2402
+ {
2403
+ description: "Create or update event contracts for a flow. Can generate contracts from existing mappings or scaffold mappings from contracts.",
2404
+ argsSchema: {
2405
+ direction: z14.string().optional().describe(
2406
+ 'Direction: "from-mappings" (extract contract from existing mappings), "from-scratch" (create new contract), or "to-mappings" (scaffold mappings from contract)'
2407
+ )
2408
+ }
2409
+ },
2410
+ async ({ direction }) => ({
2411
+ messages: [
2412
+ {
2413
+ role: "user",
2414
+ content: {
2415
+ type: "text",
2416
+ text: [
2417
+ `Help me ${direction === "to-mappings" ? "scaffold mappings from a contract" : direction === "from-mappings" ? "generate a contract from existing mappings" : "manage event contracts"}.`,
2418
+ "",
2419
+ "Follow these steps:",
2420
+ "1. Read the walkeros://reference/contract resource to understand contract structure.",
2421
+ direction === "from-mappings" ? "2. Read all destination mappings in the flow, extract referenced event fields, and generate a contract with those as required properties." : direction === "to-mappings" ? "2. Read the contract from the flow, then scaffold mapping stubs for each destination based on the contract fields." : "2. Ask for entity-action names and required properties, or ask whether to generate from existing mappings.",
2422
+ "3. Use entity-action keying with wildcards (*.*, *.action, entity.*) for broad rules.",
2423
+ "4. Define JSON Schema for events, globals, context, custom, user, and consent.",
2424
+ "5. Use flow_validate to verify the contract.",
2425
+ "",
2426
+ "Contracts and mappings are bidirectional:",
2427
+ "- **Contract \u2192 Mappings**: contract defines what events look like, mappings are scaffolded to match.",
2428
+ "- **Mappings \u2192 Contract**: existing mappings reveal which fields are used, contract formalizes them.",
2429
+ "",
2430
+ "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.",
2431
+ "",
2432
+ "Use $contract.name references to link contracts in the flow.",
2433
+ "Contracts support extends for inheritance between event types."
2434
+ ].join("\n")
2435
+ }
2436
+ }
2437
+ ]
2438
+ })
2439
+ );
2440
+ }
2441
+
2442
+ // src/prompts/use-definitions.ts
2443
+ import { z as z15 } from "zod";
2444
+ function registerUseDefinitionsPrompt(server) {
2445
+ server.registerPrompt(
2446
+ "use-definitions",
2447
+ {
2448
+ description: "Extract shared patterns into definitions and variables for DRY, environment-aware flow configurations.",
2449
+ argsSchema: {
2450
+ flowPath: z15.string().optional().describe("Path to the flow.json file to analyze")
2451
+ }
2452
+ },
2453
+ async ({ flowPath }) => ({
2454
+ messages: [
2455
+ {
2456
+ role: "user",
2457
+ content: {
2458
+ type: "text",
2459
+ text: [
2460
+ `Help me extract shared patterns into definitions and variables${flowPath ? ` in ${flowPath}` : ""}.`,
2461
+ "",
2462
+ "Follow these steps:",
2463
+ "1. Read the walkeros://reference/variables resource to understand variable syntax.",
2464
+ "2. Analyze the flow config for repeated patterns (same mapping blocks, same config values).",
2465
+ "3. Extract repeated mapping patterns into the `definitions` section with `$def.name` references.",
2466
+ "4. Extract environment-specific values into `variables` with `$var.name` references.",
2467
+ "5. Show the cascade priority: step > settings > config.",
2468
+ "6. Use flow_validate to verify the result.",
2469
+ "",
2470
+ "Variable types:",
2471
+ "- `$var.name` \u2014 variable substitution (cascade: step > settings > config)",
2472
+ "- `$env.NAME` and `$env.NAME:default` \u2014 environment variables",
2473
+ "- `$def.name` and `$def.name.path.deep` \u2014 definition references",
2474
+ "- `$contract.name` \u2014 contract references",
2475
+ "- `$code:(expr)` \u2014 inline JavaScript functions",
2476
+ "- `$store.storeId` \u2014 store injection in env values",
2477
+ "",
2478
+ "Look for:",
2479
+ "- Same API keys or URLs across multiple destinations \u2192 $var or $env",
2480
+ "- Identical mapping rules in multiple destinations \u2192 $def",
2481
+ "- Environment-specific values (dev/staging/prod) \u2192 $var with overrides"
2482
+ ].join("\n")
2483
+ }
2484
+ }
2485
+ ]
2486
+ })
2487
+ );
2488
+ }
2489
+
2490
+ // src/instructions.ts
2491
+ var SERVER_INSTRUCTIONS = `walkerOS is an open-source, privacy-first event data collection platform. Define event pipelines as code using JSON flow configurations.
2492
+
2493
+ ## Rules
2494
+
2495
+ - **Never guess package names.** Always use \`package_search\` first to find exact names, then \`package_get\` for details.
2496
+ - **Never construct flow configs from memory.** Read \`walkeros://reference/flow-schema\` and use \`package_get\` for package-specific schemas.
2497
+ - **Always validate.** Run \`flow_validate\` after every config change. If validation fails, fix and re-validate.
2498
+ - **Simulate before deploying.** Use \`flow_simulate\` to test with mocked API calls before \`flow_bundle\` or \`flow_push\`.
2499
+
2500
+ ## Workflow
2501
+
2502
+ 1. \`auth({ action: "status" })\` \u2014 check if logged in, or \`auth({ action: "login" })\` to connect
2503
+ 2. \`project_manage({ action: "list" })\` \u2014 see your projects
2504
+ 3. \`flow_manage({ action: "list" })\` \u2014 see all flows across projects
2505
+ 4. \`flow_load({ platform: "web" })\` or \`flow_load({ source: "./flow.json" })\` \u2014 create or load
2506
+ 5. \`package_search({ type: "destination", platform: "web" })\` \u2014 discover packages
2507
+ 6. \`package_get({ package: "..." })\` \u2014 read schemas, hints, examples
2508
+ 7. Use the \`add-step\` prompt \u2014 guided step addition
2509
+ 8. Use the \`setup-mapping\` prompt \u2014 event transformation config
2510
+ 9. \`flow_validate({ type: "flow", input: "flow.json" })\` \u2014 verify
2511
+ 10. \`flow_simulate({ configPath: "flow.json", event: "..." })\` \u2014 test
2512
+ 11. \`flow_manage({ action: "update", flowId: "...", content: {...} })\` \u2014 save to cloud
2513
+ 12. \`deploy_manage({ action: "deploy", flowId: "..." })\` \u2014 deploy
2514
+
2515
+ ## Architecture: Source \u2192 Collector \u2192 Destination(s)
2516
+
2517
+ Every component in a flow is a **step**: sources capture events, transformers process them, destinations deliver them, stores provide shared state. Steps connect via \`next\` (pre-collector) and \`before\` (post-collector) chains.
2518
+
2519
+ ## Flow Config Structure
2520
+
2521
+ \`\`\`json
2522
+ {
2523
+ "version": 4,
2524
+ "flows": {
2525
+ "default": {
2526
+ "config": { "platform": "web" },
2527
+ "sources": { "<name>": { "package": "<npm-package>", "config": {} } },
2528
+ "destinations": { "<name>": { "package": "<npm-package>", "config": { "settings": {} } } }
2529
+ }
2530
+ }
2531
+ }
2532
+ \`\`\`
2533
+
2534
+ - \`version: 4\` is required
2535
+ - Each flow declares its target via \`config.platform\` (\`"web"\` or \`"server"\`)
2536
+ - Destination settings go inside \`config.settings\`, not directly on the destination
2537
+ - Event format: \`{ name: "entity action", data: {...}, entity: "...", action: "..." }\`
2538
+
2539
+ ## Key Concepts
2540
+
2541
+ - **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 } }\`.
2542
+ - **Contracts** define event schemas using entity-action keying. Can generate FROM mappings or scaffold mappings FROM contracts.
2543
+ - **Variables** ($var, $env, $def, $code, $store) enable DRY, environment-aware config. Use the \`use-definitions\` prompt to extract shared patterns.
2544
+ - **Consent** gates destinations, mapping rules, and individual fields. Privacy-first by design.
2545
+
2546
+ ## Simulation Tips
2547
+
2548
+ - Destinations with \`require: ["consent"]\` stay **pending** until a \`"walker consent"\` event fires. Simulation will error "not found" for pending destinations, remove \`require\` from config when testing with \`flow_simulate\`.
2549
+ - Destinations with \`consent: { marketing: true }\` silently skip events that lack matching consent. Include \`consent\` in the event: \`{ name: "page view", data: {...}, consent: { marketing: true } }\`.
2550
+ - **Mapping** transforms event names and data at the destination level. Events without a matching mapping rule pass through unmodified.
2551
+ - **Policy** modifies the event before mapping runs, use it to inject computed fields or redact sensitive data.
2552
+
2553
+ ## Reference Resources
2554
+
2555
+ Read these before constructing configs manually: \`walkeros://reference/flow-schema\`, \`walkeros://reference/mapping\`, \`walkeros://reference/event-model\`, \`walkeros://reference/consent\`, \`walkeros://reference/variables\`, \`walkeros://reference/contract\`, \`walkeros://reference/examples\`.`;
2556
+
2557
+ // src/telemetry.ts
2558
+ import { randomUUID } from "crypto";
2559
+ import { telemetry } from "@walkeros/cli";
2560
+ async function createMcpEmitter(opts) {
2561
+ const clientName = opts.clientInfo?.name || "unknown";
2562
+ const session = randomUUID();
2563
+ const emitter = await telemetry.createEmitter({
2564
+ source: {
2565
+ type: "mcp",
2566
+ platform: "server"
2567
+ },
2568
+ packageVersion: opts.packageVersion,
2569
+ session
2570
+ });
2571
+ return {
2572
+ async emitStart() {
2573
+ const ci = telemetry.getCiInfo();
2574
+ await emitter.send("mcp start", {
2575
+ ...ci,
2576
+ client: clientName
2577
+ });
2578
+ },
2579
+ async emitInvoke(tool, outcome, timingMs) {
2580
+ await emitter.send(
2581
+ "cmd invoke",
2582
+ { outcome, client: clientName },
2583
+ timingMs,
2584
+ { tool }
2585
+ );
2586
+ },
2587
+ async emitError(kind) {
2588
+ await emitter.send("error throw", { kind });
2589
+ }
2590
+ };
2591
+ }
2592
+
2593
+ // src/server.ts
2594
+ var currentEmitter;
2595
+ function getMcpEmitterSingleton() {
2596
+ return currentEmitter;
2597
+ }
2598
+ function wrapRegisteredToolsWithTelemetry(server) {
2599
+ const internal = server;
2600
+ const tools = internal._registeredTools;
2601
+ if (!tools) return;
2602
+ for (const [toolName, tool] of Object.entries(tools)) {
2603
+ const original = tool.handler;
2604
+ tool.handler = async (...args) => {
2605
+ const start = Date.now();
2606
+ try {
2607
+ const result = await original(...args);
2608
+ const emitter = currentEmitter;
2609
+ if (emitter) {
2610
+ emitter.emitInvoke(toolName, "success", Date.now() - start).catch(() => {
2611
+ });
2612
+ }
2613
+ return result;
2614
+ } catch (err) {
2615
+ const emitter = currentEmitter;
2616
+ if (emitter) {
2617
+ emitter.emitInvoke(toolName, "error", Date.now() - start).catch(() => {
2618
+ });
2619
+ }
2620
+ throw err;
2621
+ }
2622
+ };
2623
+ }
2624
+ }
2625
+ function createWalkerOSMcpServer(opts) {
2626
+ const packageVersion = opts.version ?? "0.0.0";
2627
+ const server = new McpServer(
2628
+ {
2629
+ name: "walkeros-flow",
2630
+ version: packageVersion
2631
+ },
2632
+ { instructions: SERVER_INSTRUCTIONS }
2633
+ );
2634
+ registerAuthTool(server, opts.client);
2635
+ registerProjectManageTool(server, opts.client);
2636
+ registerFlowManageTool(server, opts.client);
2637
+ registerDeployTool(server, opts.client);
2638
+ registerFeedbackTool(server, opts.client);
2639
+ registerFlowValidateTool(server);
2640
+ registerFlowBundleTool(server);
2641
+ registerFlowSimulateTool(server);
2642
+ registerFlowPushTool(server);
2643
+ registerFlowExamplesTool(server);
2644
+ registerFlowLoadTool(server);
2645
+ registerPackageSearchTool(server);
2646
+ registerGetPackageSchemaTool(server);
2647
+ registerPackageSchemaResources(server);
2648
+ registerReferenceResources(server);
2649
+ registerAddStepPrompt(server);
2650
+ registerSetupMappingPrompt(server);
2651
+ registerManageContractPrompt(server);
2652
+ registerUseDefinitionsPrompt(server);
2653
+ wrapRegisteredToolsWithTelemetry(server);
2654
+ const priorOnInitialized = server.server.oninitialized;
2655
+ server.server.oninitialized = () => {
2656
+ try {
2657
+ priorOnInitialized?.();
2658
+ } catch {
2659
+ }
2660
+ const clientInfo = server.server.getClientVersion();
2661
+ void createMcpEmitter({
2662
+ clientInfo,
2663
+ packageVersion
2664
+ }).then(async (emitter) => {
2665
+ currentEmitter = emitter;
2666
+ await emitter.emitStart();
2667
+ }).catch(() => {
2668
+ });
2669
+ };
2670
+ return server;
2671
+ }
2672
+
2673
+ // src/http-tool-client.ts
2674
+ import {
2675
+ listProjects,
2676
+ getProject,
2677
+ createProject,
2678
+ updateProject,
2679
+ deleteProject,
2680
+ setDefaultProject,
2681
+ getDefaultProject,
2682
+ listAllFlows,
2683
+ listFlows,
2684
+ getFlow,
2685
+ createFlow,
2686
+ updateFlow,
2687
+ deleteFlow,
2688
+ duplicateFlow,
2689
+ listPreviews,
2690
+ getPreview,
2691
+ createPreview,
2692
+ deletePreview,
2693
+ deploy,
2694
+ listDeployments,
2695
+ getDeploymentBySlug,
2696
+ deleteDeployment,
2697
+ requestDeviceCode,
2698
+ pollForToken,
2699
+ whoami,
2700
+ resolveToken,
2701
+ deleteConfig,
2702
+ feedback,
2703
+ getFeedbackPreference,
2704
+ setFeedbackPreference
2705
+ } from "@walkeros/cli";
2706
+ var HttpToolClient = class {
2707
+ async listProjects() {
2708
+ return listProjects();
2709
+ }
2710
+ async getProject(options) {
2711
+ return getProject(options);
2712
+ }
2713
+ async createProject(options) {
2714
+ return createProject(options);
2715
+ }
2716
+ async updateProject(options) {
2717
+ return updateProject(options);
2718
+ }
2719
+ async deleteProject(options) {
2720
+ return deleteProject(options);
2721
+ }
2722
+ setDefaultProject(projectId) {
2723
+ setDefaultProject(projectId);
2724
+ }
2725
+ getDefaultProject() {
2726
+ return getDefaultProject();
2727
+ }
2728
+ async listAllFlows(options) {
2729
+ return listAllFlows(options);
2730
+ }
2731
+ async listFlows(options) {
2732
+ return listFlows(options);
2733
+ }
2734
+ async getFlow(options) {
2735
+ return getFlow(options);
2736
+ }
2737
+ async createFlow(options) {
2738
+ return createFlow(options);
2739
+ }
2740
+ async updateFlow(options) {
2741
+ return updateFlow(options);
2742
+ }
2743
+ async deleteFlow(options) {
2744
+ return deleteFlow(options);
2745
+ }
2746
+ async duplicateFlow(options) {
2747
+ return duplicateFlow(options);
2748
+ }
2749
+ async listPreviews(options) {
2750
+ return listPreviews(options);
2751
+ }
2752
+ async getPreview(options) {
2753
+ return getPreview(options);
2754
+ }
2755
+ async createPreview(options) {
2756
+ return createPreview(options);
2757
+ }
2758
+ async deletePreview(options) {
2759
+ return deletePreview(options);
2760
+ }
2761
+ async deploy(options) {
2762
+ return deploy(options);
2763
+ }
2764
+ async listDeployments(options) {
2765
+ return listDeployments(options);
2766
+ }
2767
+ async getDeploymentBySlug(options) {
2768
+ return getDeploymentBySlug(options);
2769
+ }
2770
+ async deleteDeployment(options) {
2771
+ return deleteDeployment(options);
2772
+ }
2773
+ async requestDeviceCode() {
2774
+ return requestDeviceCode();
2775
+ }
2776
+ async pollForToken(deviceCode, options) {
2777
+ return pollForToken(deviceCode, options);
2778
+ }
2779
+ async whoami() {
2780
+ return whoami();
2781
+ }
2782
+ resolveToken() {
2783
+ return resolveToken();
2784
+ }
2785
+ deleteConfig() {
2786
+ return deleteConfig();
2787
+ }
2788
+ async submitFeedback(text, options) {
2789
+ await feedback(text, options);
2790
+ }
2791
+ getFeedbackPreference() {
2792
+ return getFeedbackPreference();
2793
+ }
2794
+ setFeedbackPreference(anonymous) {
2795
+ setFeedbackPreference(anonymous);
2796
+ }
2797
+ };
2798
+
2799
+ // src/stdio.ts
2800
+ setClientContext({ type: "mcp", version: "4.0.0-next-1777463920154" });
2801
+ process.on("uncaughtException", (err) => {
2802
+ const emitter = getMcpEmitterSingleton();
2803
+ if (emitter) {
2804
+ emitter.emitError("uncaught").catch(() => {
2805
+ });
2806
+ }
2807
+ console.error("Uncaught exception in Flow MCP server:", err);
2808
+ });
2809
+ process.on("unhandledRejection", (reason) => {
2810
+ const emitter = getMcpEmitterSingleton();
2811
+ if (emitter) {
2812
+ emitter.emitError("unhandledRejection").catch(() => {
2813
+ });
2814
+ }
2815
+ console.error("Unhandled rejection in Flow MCP server:", reason);
2816
+ });
2817
+ async function main() {
2818
+ const server = createWalkerOSMcpServer({
2819
+ client: new HttpToolClient(),
2820
+ version: "4.0.0-next-1777463920154"
2821
+ });
2822
+ const transport = new StdioServerTransport();
2823
+ await server.connect(transport);
2824
+ console.error("walkerOS Flow MCP server running on stdio");
2825
+ }
2826
+ main().catch(async (error) => {
2827
+ try {
2828
+ const emitter = await createMcpEmitter({
2829
+ clientInfo: void 0,
2830
+ packageVersion: "4.0.0-next-1777463920154"
2831
+ });
2832
+ await emitter.emitError("startup");
2833
+ } catch {
2834
+ }
2835
+ console.error("Failed to start Flow MCP server:", error);
2836
+ process.exit(1);
2837
+ });
2838
+ //# sourceMappingURL=stdio.js.map