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