@walkeros/mcp 4.6.0 → 4.6.1
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/README.md +16 -4
- package/dist/index.d.ts +106 -5
- package/dist/index.js +430 -259
- package/dist/index.js.map +1 -1
- package/dist/stdio.js +416 -248
- package/dist/stdio.js.map +1 -1
- package/package.json +4 -4
package/dist/stdio.js
CHANGED
|
@@ -8,7 +8,7 @@ import { setClientContext } from "@walkeros/cli";
|
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
9
|
|
|
10
10
|
// src/tools/validate.ts
|
|
11
|
-
import { validate
|
|
11
|
+
import { validate } from "@walkeros/cli";
|
|
12
12
|
import { schemas } from "@walkeros/cli/dev";
|
|
13
13
|
import { mcpResult, mcpError } from "@walkeros/core";
|
|
14
14
|
|
|
@@ -102,7 +102,54 @@ var ExamplesListOutputShape = {
|
|
|
102
102
|
).describe("Step examples")
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
// src/runtime/types.ts
|
|
106
|
+
var RuntimeRefusal = class extends Error {
|
|
107
|
+
hint;
|
|
108
|
+
constructor(message, hint) {
|
|
109
|
+
super(message);
|
|
110
|
+
this.name = "RuntimeRefusal";
|
|
111
|
+
this.hint = hint;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function refusalHint(error, fallback) {
|
|
115
|
+
return error instanceof RuntimeRefusal ? error.hint : fallback;
|
|
116
|
+
}
|
|
117
|
+
var HINT_OUT_OF_PROCESS = "Build and deploy through the app with deploy_manage, or simulate the flow in the app. On your own machine, use the walkerOS CLI.";
|
|
118
|
+
function unavailableOperation(operation) {
|
|
119
|
+
const verb = operation === "bundle" ? "Bundling" : operation === "simulate" ? "Simulating" : "Running";
|
|
120
|
+
return new RuntimeRefusal(
|
|
121
|
+
`${verb} a flow is not available on the hosted walkerOS MCP server.`,
|
|
122
|
+
HINT_OUT_OF_PROCESS
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/cloud-flow.ts
|
|
127
|
+
import { isObject } from "@walkeros/core";
|
|
128
|
+
var CLOUD_ID_PATTERN = /^(flow|cfg)_[A-Za-z0-9_-]+$/;
|
|
129
|
+
function isCloudId(input) {
|
|
130
|
+
return CLOUD_ID_PATTERN.test(input);
|
|
131
|
+
}
|
|
132
|
+
function flowConfigOf(flow) {
|
|
133
|
+
return isObject(flow) && isObject(flow.config) ? flow.config : {};
|
|
134
|
+
}
|
|
135
|
+
|
|
105
136
|
// src/tools/validate.ts
|
|
137
|
+
async function loadValidateInput(runtime, input, type) {
|
|
138
|
+
if (!input || input.trim() === "") throw new Error(`${type} is required`);
|
|
139
|
+
const trimmed = input.trim();
|
|
140
|
+
const looksLikeJson = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
141
|
+
try {
|
|
142
|
+
return await runtime.load(input);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error instanceof RuntimeRefusal || isCloudId(trimmed)) throw error;
|
|
145
|
+
if (looksLikeJson) {
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
throw new Error(`Failed to parse ${type}. ${message}`);
|
|
148
|
+
}
|
|
149
|
+
if (type === "event") return { name: trimmed };
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
106
153
|
var DEPRECATED_STORE_PACKAGE = "@walkeros/store-memory";
|
|
107
154
|
function detectDeprecatedStorePackages(config) {
|
|
108
155
|
const errors = [];
|
|
@@ -132,7 +179,7 @@ function detectDeprecatedStorePackages(config) {
|
|
|
132
179
|
return errors;
|
|
133
180
|
}
|
|
134
181
|
var TITLE = "Validate Flow";
|
|
135
|
-
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.";
|
|
182
|
+
var DESCRIPTION = "Validate walkerOS events, flow configurations, mapping rules, or data contracts. Accepts JSON strings, file paths, or URLs as input; on the hosted server only inline JSON or a saved flow id (flow_ or cfg_), no file paths or URLs. Returns validation results with errors, warnings, and details.";
|
|
136
183
|
var inputSchema = schemas.ValidateInputShape;
|
|
137
184
|
var annotations = {
|
|
138
185
|
readOnlyHint: true,
|
|
@@ -140,17 +187,17 @@ var annotations = {
|
|
|
140
187
|
idempotentHint: true,
|
|
141
188
|
openWorldHint: false
|
|
142
189
|
};
|
|
143
|
-
function createFlowValidateToolSpec() {
|
|
190
|
+
function createFlowValidateToolSpec(runtime) {
|
|
144
191
|
return {
|
|
145
192
|
name: "flow_validate",
|
|
146
193
|
title: TITLE,
|
|
147
194
|
description: DESCRIPTION,
|
|
148
195
|
inputSchema,
|
|
149
196
|
annotations,
|
|
150
|
-
handler: (input) => flowValidateHandlerBody(input)
|
|
197
|
+
handler: (input) => flowValidateHandlerBody(runtime, input)
|
|
151
198
|
};
|
|
152
199
|
}
|
|
153
|
-
async function flowValidateHandlerBody(input) {
|
|
200
|
+
async function flowValidateHandlerBody(runtime, input) {
|
|
154
201
|
const {
|
|
155
202
|
type,
|
|
156
203
|
input: validateInput,
|
|
@@ -158,30 +205,27 @@ async function flowValidateHandlerBody(input) {
|
|
|
158
205
|
path: path2
|
|
159
206
|
} = input ?? {};
|
|
160
207
|
try {
|
|
161
|
-
const
|
|
208
|
+
const resolved = await loadValidateInput(runtime, validateInput, type);
|
|
209
|
+
const result = await validate(type, resolved, {
|
|
162
210
|
flow,
|
|
163
211
|
path: path2
|
|
164
212
|
});
|
|
165
213
|
let augmented = result;
|
|
166
|
-
if (type === "flow"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
errors: [...result.errors, ...deprecatedErrors]
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
} catch {
|
|
214
|
+
if (type === "flow") {
|
|
215
|
+
const deprecatedErrors = detectDeprecatedStorePackages(resolved);
|
|
216
|
+
if (deprecatedErrors.length > 0) {
|
|
217
|
+
augmented = {
|
|
218
|
+
...result,
|
|
219
|
+
valid: false,
|
|
220
|
+
errors: [...result.errors, ...deprecatedErrors]
|
|
221
|
+
};
|
|
178
222
|
}
|
|
179
223
|
}
|
|
180
224
|
const hints = augmented.valid ? {
|
|
181
|
-
next: [
|
|
225
|
+
next: runtime.simulate ? [
|
|
182
226
|
"Use flow_simulate to test event flow",
|
|
183
227
|
"Use flow_bundle to build"
|
|
184
|
-
]
|
|
228
|
+
] : [HINT_OUT_OF_PROCESS]
|
|
185
229
|
} : {
|
|
186
230
|
next: [
|
|
187
231
|
"Fix errors above, then run flow_validate again",
|
|
@@ -192,12 +236,15 @@ async function flowValidateHandlerBody(input) {
|
|
|
192
236
|
} catch (error) {
|
|
193
237
|
return mcpError(
|
|
194
238
|
error,
|
|
195
|
-
|
|
239
|
+
refusalHint(
|
|
240
|
+
error,
|
|
241
|
+
"Check the input parameter \u2014 expected a JSON string, file path, or URL"
|
|
242
|
+
)
|
|
196
243
|
);
|
|
197
244
|
}
|
|
198
245
|
}
|
|
199
|
-
function registerFlowValidateTool(server) {
|
|
200
|
-
const spec = createFlowValidateToolSpec();
|
|
246
|
+
function registerFlowValidateTool(server, runtime) {
|
|
247
|
+
const spec = createFlowValidateToolSpec(runtime);
|
|
201
248
|
server.registerTool(
|
|
202
249
|
spec.name,
|
|
203
250
|
{
|
|
@@ -215,17 +262,14 @@ function registerFlowValidateTool(server) {
|
|
|
215
262
|
}
|
|
216
263
|
|
|
217
264
|
// src/tools/bundle.ts
|
|
218
|
-
import { bundle } from "@walkeros/cli";
|
|
219
265
|
import { schemas as schemas2 } from "@walkeros/cli/dev";
|
|
220
266
|
import { mcpResult as mcpResult2, mcpError as mcpError2 } from "@walkeros/core";
|
|
221
267
|
|
|
222
268
|
// src/tools/resolve-config-path.ts
|
|
223
|
-
var API_ID_PREFIX = /^(flow|cfg)_/;
|
|
224
269
|
async function resolveConfigPath(client, configPath) {
|
|
225
|
-
if (!
|
|
270
|
+
if (!isCloudId(configPath)) return configPath;
|
|
226
271
|
const flow = await client.getFlow({ flowId: configPath });
|
|
227
|
-
|
|
228
|
-
return JSON.stringify(config ?? {});
|
|
272
|
+
return JSON.stringify(flowConfigOf(flow));
|
|
229
273
|
}
|
|
230
274
|
|
|
231
275
|
// src/tools/bundle.ts
|
|
@@ -240,24 +284,28 @@ var annotations2 = {
|
|
|
240
284
|
idempotentHint: false,
|
|
241
285
|
openWorldHint: true
|
|
242
286
|
};
|
|
243
|
-
function createFlowBundleToolSpec(client) {
|
|
287
|
+
function createFlowBundleToolSpec(client, runtime) {
|
|
244
288
|
return {
|
|
245
289
|
name: "flow_bundle",
|
|
246
290
|
title: TITLE2,
|
|
247
291
|
description: DESCRIPTION2,
|
|
248
292
|
inputSchema: inputSchema2,
|
|
249
293
|
annotations: annotations2,
|
|
250
|
-
handler: (input) => flowBundleHandlerBody(client, input)
|
|
294
|
+
handler: (input) => flowBundleHandlerBody(client, runtime, input)
|
|
251
295
|
};
|
|
252
296
|
}
|
|
253
|
-
async function flowBundleHandlerBody(client, input) {
|
|
297
|
+
async function flowBundleHandlerBody(client, runtime, input) {
|
|
254
298
|
const { configPath, flow, stats, output } = input ?? {};
|
|
299
|
+
if (!runtime.bundle) {
|
|
300
|
+
const refusal = unavailableOperation("bundle");
|
|
301
|
+
return mcpError2(refusal, refusal.hint);
|
|
302
|
+
}
|
|
255
303
|
try {
|
|
256
304
|
const resolvedConfigPath = await resolveConfigPath(client, configPath);
|
|
257
|
-
const result = await bundle(resolvedConfigPath, {
|
|
305
|
+
const result = await runtime.bundle(resolvedConfigPath, {
|
|
258
306
|
flowName: flow,
|
|
259
307
|
stats: stats ?? true,
|
|
260
|
-
|
|
308
|
+
output
|
|
261
309
|
});
|
|
262
310
|
if (!result) {
|
|
263
311
|
return mcpResult2(
|
|
@@ -281,11 +329,14 @@ async function flowBundleHandlerBody(client, input) {
|
|
|
281
329
|
}
|
|
282
330
|
);
|
|
283
331
|
} catch (error) {
|
|
284
|
-
return mcpError2(
|
|
332
|
+
return mcpError2(
|
|
333
|
+
error,
|
|
334
|
+
refusalHint(error, "Run flow_validate for detailed error messages")
|
|
335
|
+
);
|
|
285
336
|
}
|
|
286
337
|
}
|
|
287
|
-
function registerFlowBundleTool(server, client) {
|
|
288
|
-
const spec = createFlowBundleToolSpec(client);
|
|
338
|
+
function registerFlowBundleTool(server, client, runtime) {
|
|
339
|
+
const spec = createFlowBundleToolSpec(client, runtime);
|
|
289
340
|
server.registerTool(
|
|
290
341
|
spec.name,
|
|
291
342
|
{
|
|
@@ -304,101 +355,17 @@ function registerFlowBundleTool(server, client) {
|
|
|
304
355
|
|
|
305
356
|
// src/tools/simulate.ts
|
|
306
357
|
import { z as z2 } from "zod";
|
|
307
|
-
import {
|
|
308
|
-
simulateSource,
|
|
309
|
-
simulateTransformer,
|
|
310
|
-
simulateDestination,
|
|
311
|
-
simulateCollector
|
|
312
|
-
} from "@walkeros/cli";
|
|
313
358
|
import { schemas as schemas3 } from "@walkeros/cli/dev";
|
|
314
359
|
import { mcpResult as mcpResult3, mcpError as mcpError3 } from "@walkeros/core";
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
var MAX_ENTRIES = 8;
|
|
324
|
-
var cache = /* @__PURE__ */ new Map();
|
|
325
|
-
var inFlight = /* @__PURE__ */ new Map();
|
|
326
|
-
var cleanupRegistered = false;
|
|
327
|
-
function hashConfig(resolvedConfig) {
|
|
328
|
-
return createHash("sha256").update(resolvedConfig).digest("hex");
|
|
329
|
-
}
|
|
330
|
-
function isInlineJsonConfig(resolvedConfig) {
|
|
331
|
-
const trimmed = resolvedConfig.trimStart();
|
|
332
|
-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
|
|
333
|
-
try {
|
|
334
|
-
JSON.parse(resolvedConfig);
|
|
335
|
-
return true;
|
|
336
|
-
} catch {
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
function registerProcessCleanup() {
|
|
341
|
-
if (cleanupRegistered) return;
|
|
342
|
-
cleanupRegistered = true;
|
|
343
|
-
const cleanup = () => {
|
|
344
|
-
for (const entry of cache.values()) {
|
|
345
|
-
try {
|
|
346
|
-
rmSync(entry.dir, { recursive: true, force: true });
|
|
347
|
-
} catch {
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
cache.clear();
|
|
351
|
-
};
|
|
352
|
-
process.once("exit", cleanup);
|
|
353
|
-
}
|
|
354
|
-
async function evictIfNeeded() {
|
|
355
|
-
while (cache.size > MAX_ENTRIES) {
|
|
356
|
-
const oldestKey = cache.keys().next().value;
|
|
357
|
-
if (oldestKey === void 0) break;
|
|
358
|
-
const evicted = cache.get(oldestKey);
|
|
359
|
-
cache.delete(oldestKey);
|
|
360
|
-
if (evicted) await rm(evicted.dir, { recursive: true, force: true });
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
async function getOrBuildBundle(resolvedConfig) {
|
|
364
|
-
if (!isInlineJsonConfig(resolvedConfig)) return void 0;
|
|
365
|
-
registerProcessCleanup();
|
|
366
|
-
const key = hashConfig(resolvedConfig);
|
|
367
|
-
const cached = cache.get(key);
|
|
368
|
-
if (cached) {
|
|
369
|
-
cache.delete(key);
|
|
370
|
-
cache.set(key, cached);
|
|
371
|
-
return cached.bundlePath;
|
|
372
|
-
}
|
|
373
|
-
const pending = inFlight.get(key);
|
|
374
|
-
if (pending) return pending;
|
|
375
|
-
const build = (async () => {
|
|
376
|
-
const dir = path.join(
|
|
377
|
-
os.tmpdir(),
|
|
378
|
-
`walkeros-mcp-bundle-${key.slice(0, 16)}`
|
|
379
|
-
);
|
|
380
|
-
await rm(dir, { recursive: true, force: true });
|
|
381
|
-
await mkdir(dir, { recursive: true });
|
|
382
|
-
const bundlePath = path.join(dir, "flow.mjs");
|
|
383
|
-
await writeFile(bundlePath, "", "utf-8");
|
|
384
|
-
await bundle2(resolvedConfig, {
|
|
385
|
-
target: "simulate",
|
|
386
|
-
silent: true,
|
|
387
|
-
buildOverrides: { output: bundlePath, format: "esm", minify: false }
|
|
388
|
-
});
|
|
389
|
-
cache.set(key, { bundlePath, dir });
|
|
390
|
-
await evictIfNeeded();
|
|
391
|
-
return bundlePath;
|
|
392
|
-
})();
|
|
393
|
-
inFlight.set(key, build);
|
|
394
|
-
try {
|
|
395
|
-
return await build;
|
|
396
|
-
} finally {
|
|
397
|
-
inFlight.delete(key);
|
|
398
|
-
}
|
|
360
|
+
var STEP_TYPES = [
|
|
361
|
+
"source",
|
|
362
|
+
"transformer",
|
|
363
|
+
"collector",
|
|
364
|
+
"destination"
|
|
365
|
+
];
|
|
366
|
+
function isStepType(value) {
|
|
367
|
+
return STEP_TYPES.some((t) => t === value);
|
|
399
368
|
}
|
|
400
|
-
|
|
401
|
-
// src/tools/simulate.ts
|
|
402
369
|
var TITLE3 = "Simulate Flow";
|
|
403
370
|
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? } }, where content is the walkerOS event { name: "entity action", data: {...} }. step (required) targets the step to simulate, e.g. "destination.gtag". 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.';
|
|
404
371
|
var inputSchema3 = {
|
|
@@ -427,23 +394,31 @@ var inputSchema3 = {
|
|
|
427
394
|
)
|
|
428
395
|
};
|
|
429
396
|
var annotations3 = {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
397
|
+
// Simulation downloads the packages a config names and runs caller-controlled
|
|
398
|
+
// flow code in process. Destinations are mocked, but that code is not, so the
|
|
399
|
+
// hints stay conservative: side effects possible, repeat calls not assumed
|
|
400
|
+
// safe, external systems reachable.
|
|
401
|
+
readOnlyHint: false,
|
|
402
|
+
destructiveHint: true,
|
|
403
|
+
idempotentHint: false,
|
|
404
|
+
openWorldHint: true
|
|
434
405
|
};
|
|
435
|
-
function createFlowSimulateToolSpec(client) {
|
|
406
|
+
function createFlowSimulateToolSpec(client, runtime) {
|
|
436
407
|
return {
|
|
437
408
|
name: "flow_simulate",
|
|
438
409
|
title: TITLE3,
|
|
439
410
|
description: DESCRIPTION3,
|
|
440
411
|
inputSchema: inputSchema3,
|
|
441
412
|
annotations: annotations3,
|
|
442
|
-
handler: (input) => flowSimulateHandlerBody(client, input)
|
|
413
|
+
handler: (input) => flowSimulateHandlerBody(client, runtime, input)
|
|
443
414
|
};
|
|
444
415
|
}
|
|
445
|
-
async function flowSimulateHandlerBody(client, input) {
|
|
416
|
+
async function flowSimulateHandlerBody(client, runtime, input) {
|
|
446
417
|
const { configPath, event, flow, platform, step, verbose, ingest, state } = input ?? {};
|
|
418
|
+
if (!runtime.simulate) {
|
|
419
|
+
const refusal = unavailableOperation("simulate");
|
|
420
|
+
return mcpError3(refusal, refusal.hint);
|
|
421
|
+
}
|
|
447
422
|
try {
|
|
448
423
|
if (!event) {
|
|
449
424
|
throw new Error(
|
|
@@ -473,66 +448,16 @@ async function flowSimulateHandlerBody(client, input) {
|
|
|
473
448
|
}
|
|
474
449
|
const stepType = step.substring(0, dotIndex);
|
|
475
450
|
const stepId = step.substring(dotIndex + 1);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
} catch {
|
|
481
|
-
bundlePath = void 0;
|
|
482
|
-
}
|
|
483
|
-
let result;
|
|
484
|
-
switch (stepType) {
|
|
485
|
-
case "source":
|
|
486
|
-
result = await simulateSource(resolvedConfigPath, resolvedEvent, {
|
|
487
|
-
sourceId: stepId,
|
|
488
|
-
bundlePath,
|
|
489
|
-
flow,
|
|
490
|
-
silent: true
|
|
491
|
-
});
|
|
492
|
-
break;
|
|
493
|
-
case "transformer":
|
|
494
|
-
result = await simulateTransformer(
|
|
495
|
-
resolvedConfigPath,
|
|
496
|
-
resolvedEvent,
|
|
497
|
-
{
|
|
498
|
-
transformerId: stepId,
|
|
499
|
-
bundlePath,
|
|
500
|
-
flow,
|
|
501
|
-
silent: true,
|
|
502
|
-
ingest
|
|
503
|
-
}
|
|
504
|
-
);
|
|
505
|
-
break;
|
|
506
|
-
case "collector":
|
|
507
|
-
result = await simulateCollector(
|
|
508
|
-
resolvedConfigPath,
|
|
509
|
-
resolvedEvent,
|
|
510
|
-
{
|
|
511
|
-
collectorName: stepId,
|
|
512
|
-
bundlePath,
|
|
513
|
-
flow,
|
|
514
|
-
silent: true,
|
|
515
|
-
state
|
|
516
|
-
}
|
|
517
|
-
);
|
|
518
|
-
break;
|
|
519
|
-
case "destination":
|
|
520
|
-
result = await simulateDestination(
|
|
521
|
-
resolvedConfigPath,
|
|
522
|
-
resolvedEvent,
|
|
523
|
-
{
|
|
524
|
-
destinationId: stepId,
|
|
525
|
-
bundlePath,
|
|
526
|
-
flow,
|
|
527
|
-
silent: true
|
|
528
|
-
}
|
|
529
|
-
);
|
|
530
|
-
break;
|
|
531
|
-
default:
|
|
532
|
-
throw new Error(
|
|
533
|
-
`Unknown step type "${stepType}". Use "source", "collector", "transformer", or "destination".`
|
|
534
|
-
);
|
|
451
|
+
if (!isStepType(stepType)) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Unknown step type "${stepType}". Use "source", "collector", "transformer", or "destination".`
|
|
454
|
+
);
|
|
535
455
|
}
|
|
456
|
+
const resolvedConfigPath = await resolveConfigPath(client, configPath);
|
|
457
|
+
const result = await runtime.simulate(
|
|
458
|
+
resolvedConfigPath,
|
|
459
|
+
{ stepType, stepId, event: resolvedEvent, flow, ingest, state }
|
|
460
|
+
);
|
|
536
461
|
const success = !result.error;
|
|
537
462
|
const errorMessage = result.error?.message;
|
|
538
463
|
if (result.step === "source") {
|
|
@@ -620,11 +545,11 @@ async function flowSimulateHandlerBody(client, input) {
|
|
|
620
545
|
if (msg.includes("not found in collector")) {
|
|
621
546
|
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.';
|
|
622
547
|
}
|
|
623
|
-
return mcpError3(error, hint);
|
|
548
|
+
return mcpError3(error, refusalHint(error, hint));
|
|
624
549
|
}
|
|
625
550
|
}
|
|
626
|
-
function registerFlowSimulateTool(server, client) {
|
|
627
|
-
const spec = createFlowSimulateToolSpec(client);
|
|
551
|
+
function registerFlowSimulateTool(server, client, runtime) {
|
|
552
|
+
const spec = createFlowSimulateToolSpec(client, runtime);
|
|
628
553
|
server.registerTool(
|
|
629
554
|
spec.name,
|
|
630
555
|
{
|
|
@@ -643,7 +568,6 @@ function registerFlowSimulateTool(server, client) {
|
|
|
643
568
|
|
|
644
569
|
// src/tools/push.ts
|
|
645
570
|
import { z as z3 } from "zod";
|
|
646
|
-
import { push } from "@walkeros/cli";
|
|
647
571
|
import { schemas as schemas4 } from "@walkeros/cli/dev";
|
|
648
572
|
import { mcpResult as mcpResult4, mcpError as mcpError4 } from "@walkeros/core";
|
|
649
573
|
var TITLE4 = "Push Events";
|
|
@@ -662,21 +586,24 @@ var annotations4 = {
|
|
|
662
586
|
idempotentHint: false,
|
|
663
587
|
openWorldHint: true
|
|
664
588
|
};
|
|
665
|
-
function createFlowPushToolSpec() {
|
|
589
|
+
function createFlowPushToolSpec(runtime) {
|
|
666
590
|
return {
|
|
667
591
|
name: "flow_push",
|
|
668
592
|
title: TITLE4,
|
|
669
593
|
description: DESCRIPTION4,
|
|
670
594
|
inputSchema: inputSchema4,
|
|
671
595
|
annotations: annotations4,
|
|
672
|
-
handler: (input) => flowPushHandlerBody(input)
|
|
596
|
+
handler: (input) => flowPushHandlerBody(runtime, input)
|
|
673
597
|
};
|
|
674
598
|
}
|
|
675
|
-
async function flowPushHandlerBody(input) {
|
|
599
|
+
async function flowPushHandlerBody(runtime, input) {
|
|
676
600
|
const { configPath, event, flow, platform } = input ?? {};
|
|
601
|
+
if (!runtime.push) {
|
|
602
|
+
const refusal = unavailableOperation("push");
|
|
603
|
+
return mcpError4(refusal, refusal.hint);
|
|
604
|
+
}
|
|
677
605
|
try {
|
|
678
|
-
const result = await push(configPath, event, {
|
|
679
|
-
json: true,
|
|
606
|
+
const result = await runtime.push(configPath, event, {
|
|
680
607
|
flow,
|
|
681
608
|
platform
|
|
682
609
|
});
|
|
@@ -690,12 +617,15 @@ async function flowPushHandlerBody(input) {
|
|
|
690
617
|
} catch (error) {
|
|
691
618
|
return mcpError4(
|
|
692
619
|
error,
|
|
693
|
-
|
|
620
|
+
refusalHint(
|
|
621
|
+
error,
|
|
622
|
+
"Check configPath and event format. For web flows, use flow_simulate."
|
|
623
|
+
)
|
|
694
624
|
);
|
|
695
625
|
}
|
|
696
626
|
}
|
|
697
|
-
function registerFlowPushTool(server) {
|
|
698
|
-
const spec = createFlowPushToolSpec();
|
|
627
|
+
function registerFlowPushTool(server, runtime) {
|
|
628
|
+
const spec = createFlowPushToolSpec(runtime);
|
|
699
629
|
server.registerTool(
|
|
700
630
|
spec.name,
|
|
701
631
|
{
|
|
@@ -714,7 +644,6 @@ function registerFlowPushTool(server) {
|
|
|
714
644
|
|
|
715
645
|
// src/tools/examples.ts
|
|
716
646
|
import { z as z4 } from "zod";
|
|
717
|
-
import { loadJsonConfig as loadJsonConfig2 } from "@walkeros/cli";
|
|
718
647
|
import { fetchPackage, mcpResult as mcpResult5, mcpError as mcpError5 } from "@walkeros/core";
|
|
719
648
|
|
|
720
649
|
// src/catalog.ts
|
|
@@ -722,7 +651,7 @@ var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
|
|
|
722
651
|
var JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm";
|
|
723
652
|
var WALKEROS_JSON_PATH = "dist/walkerOS.json";
|
|
724
653
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
725
|
-
var CLIENT_HEADER = "walkeros-mcp/4.6.
|
|
654
|
+
var CLIENT_HEADER = "walkeros-mcp/4.6.1";
|
|
726
655
|
function getPackageBaseUrl() {
|
|
727
656
|
return process.env.WALKEROS_APP_URL || void 0;
|
|
728
657
|
}
|
|
@@ -730,7 +659,7 @@ var lastFetchInfo;
|
|
|
730
659
|
function getLastCatalogSource() {
|
|
731
660
|
return lastFetchInfo;
|
|
732
661
|
}
|
|
733
|
-
var
|
|
662
|
+
var cache = /* @__PURE__ */ new Map();
|
|
734
663
|
function normalizePlatform(platform) {
|
|
735
664
|
if (platform == null) return [];
|
|
736
665
|
if (typeof platform === "string") {
|
|
@@ -744,7 +673,7 @@ function normalizePlatform(platform) {
|
|
|
744
673
|
async function fetchCatalog(filters) {
|
|
745
674
|
const sourceKey = filters?.baseUrl ?? "npm";
|
|
746
675
|
const warnings = [];
|
|
747
|
-
const cached =
|
|
676
|
+
const cached = cache.get(sourceKey);
|
|
748
677
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
749
678
|
lastFetchInfo = {
|
|
750
679
|
source: filters?.baseUrl ? "app" : "npm",
|
|
@@ -802,7 +731,7 @@ async function fetchCatalog(filters) {
|
|
|
802
731
|
timestamp: Date.now()
|
|
803
732
|
};
|
|
804
733
|
if (result.entries.length > 0 && result.complete) {
|
|
805
|
-
|
|
734
|
+
cache.set(sourceKey, { entries: result.entries, timestamp: Date.now() });
|
|
806
735
|
}
|
|
807
736
|
return { entries: applyFilters(result.entries, filters), warnings };
|
|
808
737
|
}
|
|
@@ -875,10 +804,13 @@ function applyFilters(entries, filters) {
|
|
|
875
804
|
}
|
|
876
805
|
|
|
877
806
|
// src/tools/examples.ts
|
|
807
|
+
var MAX_PACKAGE_LOOKUPS = 25;
|
|
878
808
|
var TITLE5 = "Flow Examples";
|
|
879
|
-
var DESCRIPTION5 = 'List all step examples in a walkerOS flow configuration. Shows example names, step locations, and in/out shapes. Inline examples on a step take precedence; steps without inline examples fall back to the examples shipped by their referenced package. Each result is tagged with its source ("inline" or "package"). Use this to discover available test fixtures and simulation data.';
|
|
809
|
+
var DESCRIPTION5 = 'List all step examples in a walkerOS flow configuration. Shows example names, step locations, and in/out shapes. Inline examples on a step take precedence; steps without inline examples fall back to the examples shipped by their referenced package. Each result is tagged with its source ("inline" or "package"). On the hosted server configPath accepts only inline JSON or a saved flow id (flow_ or cfg_), no file paths or URLs. Use this to discover available test fixtures and simulation data.';
|
|
880
810
|
var inputSchema5 = {
|
|
881
|
-
configPath: z4.string().min(1).describe(
|
|
811
|
+
configPath: z4.string().min(1).describe(
|
|
812
|
+
"Inline JSON string, file path, or URL of a flow configuration (hosted server: inline JSON or a saved flow id only)"
|
|
813
|
+
),
|
|
882
814
|
flow: z4.string().optional().describe("Flow name for multi-flow configs"),
|
|
883
815
|
step: z4.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
|
|
884
816
|
full: z4.boolean().optional().describe(
|
|
@@ -894,20 +826,20 @@ var annotations5 = {
|
|
|
894
826
|
idempotentHint: true,
|
|
895
827
|
openWorldHint: false
|
|
896
828
|
};
|
|
897
|
-
function createFlowExamplesToolSpec() {
|
|
829
|
+
function createFlowExamplesToolSpec(runtime) {
|
|
898
830
|
return {
|
|
899
831
|
name: "flow_examples",
|
|
900
832
|
title: TITLE5,
|
|
901
833
|
description: DESCRIPTION5,
|
|
902
834
|
inputSchema: inputSchema5,
|
|
903
835
|
annotations: annotations5,
|
|
904
|
-
handler: (input) => flowExamplesHandlerBody(input)
|
|
836
|
+
handler: (input) => flowExamplesHandlerBody(runtime, input)
|
|
905
837
|
};
|
|
906
838
|
}
|
|
907
|
-
async function flowExamplesHandlerBody(input) {
|
|
839
|
+
async function flowExamplesHandlerBody(runtime, input) {
|
|
908
840
|
const { configPath, flow, step, full, includeHidden } = input ?? {};
|
|
909
841
|
try {
|
|
910
|
-
const rawConfig = await
|
|
842
|
+
const rawConfig = await runtime.load(configPath);
|
|
911
843
|
const flowNames = Object.keys(rawConfig.flows || {});
|
|
912
844
|
const flowName = flow || (flowNames.length === 1 ? flowNames[0] : void 0);
|
|
913
845
|
if (!flowName) {
|
|
@@ -961,6 +893,19 @@ async function flowExamplesHandlerBody(input) {
|
|
|
961
893
|
}
|
|
962
894
|
return void 0;
|
|
963
895
|
};
|
|
896
|
+
const packageLookups = /* @__PURE__ */ new Map();
|
|
897
|
+
let skippedPackages = false;
|
|
898
|
+
const examplesForPackage = (packageName) => {
|
|
899
|
+
const existing = packageLookups.get(packageName);
|
|
900
|
+
if (existing) return existing;
|
|
901
|
+
if (packageLookups.size >= MAX_PACKAGE_LOOKUPS) {
|
|
902
|
+
skippedPackages = true;
|
|
903
|
+
return Promise.resolve(void 0);
|
|
904
|
+
}
|
|
905
|
+
const lookup = loadPackageExamples(packageName);
|
|
906
|
+
packageLookups.set(packageName, lookup);
|
|
907
|
+
return lookup;
|
|
908
|
+
};
|
|
964
909
|
const stepTypes = [
|
|
965
910
|
{ key: "sources", type: "source" },
|
|
966
911
|
{ key: "transformers", type: "transformer" },
|
|
@@ -977,7 +922,7 @@ async function flowExamplesHandlerBody(input) {
|
|
|
977
922
|
continue;
|
|
978
923
|
}
|
|
979
924
|
if (!ref.package) continue;
|
|
980
|
-
const packageExamples = await
|
|
925
|
+
const packageExamples = await examplesForPackage(ref.package);
|
|
981
926
|
if (packageExamples)
|
|
982
927
|
examples.push(...toItems(packageExamples, type, name, "package"));
|
|
983
928
|
}
|
|
@@ -987,21 +932,33 @@ async function flowExamplesHandlerBody(input) {
|
|
|
987
932
|
count: examples.length,
|
|
988
933
|
examples
|
|
989
934
|
};
|
|
990
|
-
const
|
|
991
|
-
next: ["Use flow_simulate with step and event to simulate"]
|
|
992
|
-
};
|
|
935
|
+
const warnings = [];
|
|
993
936
|
if (examples.length === 0) {
|
|
994
|
-
|
|
937
|
+
warnings.push(
|
|
995
938
|
"No examples found. Add examples to step entries, or reference a package that ships examples (see package_get)."
|
|
996
|
-
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
if (skippedPackages) {
|
|
942
|
+
warnings.push(
|
|
943
|
+
`Package examples were looked up for the first ${MAX_PACKAGE_LOOKUPS} packages only. Use step to narrow the result.`
|
|
944
|
+
);
|
|
997
945
|
}
|
|
946
|
+
const hints = {
|
|
947
|
+
next: [
|
|
948
|
+
runtime.simulate ? "Use flow_simulate with step and event to simulate" : HINT_OUT_OF_PROCESS
|
|
949
|
+
],
|
|
950
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
951
|
+
};
|
|
998
952
|
return mcpResult5(result, hints);
|
|
999
953
|
} catch (error) {
|
|
1000
|
-
return mcpError5(
|
|
954
|
+
return mcpError5(
|
|
955
|
+
error,
|
|
956
|
+
refusalHint(error, "Check configPath \u2014 expected a flow.json file")
|
|
957
|
+
);
|
|
1001
958
|
}
|
|
1002
959
|
}
|
|
1003
|
-
function registerFlowExamplesTool(server) {
|
|
1004
|
-
const spec = createFlowExamplesToolSpec();
|
|
960
|
+
function registerFlowExamplesTool(server, runtime) {
|
|
961
|
+
const spec = createFlowExamplesToolSpec(runtime);
|
|
1005
962
|
server.registerTool(
|
|
1006
963
|
spec.name,
|
|
1007
964
|
{
|
|
@@ -1212,7 +1169,6 @@ function registerGetPackageSchemaTool(server) {
|
|
|
1212
1169
|
|
|
1213
1170
|
// src/tools/flow-load.ts
|
|
1214
1171
|
import { z as z6 } from "zod";
|
|
1215
|
-
import { loadJsonConfig as loadJsonConfig3 } from "@walkeros/cli";
|
|
1216
1172
|
import { mcpResult as mcpResult7, mcpError as mcpError7 } from "@walkeros/core";
|
|
1217
1173
|
|
|
1218
1174
|
// src/types.ts
|
|
@@ -1297,7 +1253,6 @@ function resolveDefaultProject(client, projectId) {
|
|
|
1297
1253
|
}
|
|
1298
1254
|
|
|
1299
1255
|
// src/tools/flow-load.ts
|
|
1300
|
-
var API_ID_PREFIX2 = /^(flow|cfg)_/;
|
|
1301
1256
|
var WEB_SKELETON = {
|
|
1302
1257
|
version: 4,
|
|
1303
1258
|
flows: {
|
|
@@ -1319,7 +1274,7 @@ var SERVER_SKELETON = {
|
|
|
1319
1274
|
}
|
|
1320
1275
|
};
|
|
1321
1276
|
var TITLE6 = "Load or Create Flow";
|
|
1322
|
-
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.";
|
|
1277
|
+
var DESCRIPTION6 = "Load an existing flow configuration from a local file path, URL, or walkerOS API (by flow ID). On the hosted server only inline JSON or a saved flow id is accepted, no file paths or URLs. Or create a new empty flow by specifying a platform (web or server). Use the add-step prompt to add sources, destinations, transformers, or stores to the flow.";
|
|
1323
1278
|
var inputSchema6 = {
|
|
1324
1279
|
source: z6.string().optional().describe(
|
|
1325
1280
|
"Flow source: local file path (./flow.json), URL (https://...), inline JSON string, or API flow ID (cfg_...). Omit to create a new flow."
|
|
@@ -1338,19 +1293,19 @@ var annotations6 = {
|
|
|
1338
1293
|
idempotentHint: true,
|
|
1339
1294
|
openWorldHint: true
|
|
1340
1295
|
};
|
|
1341
|
-
function createFlowLoadToolSpec(client) {
|
|
1296
|
+
function createFlowLoadToolSpec(client, runtime) {
|
|
1342
1297
|
return {
|
|
1343
1298
|
name: "flow_load",
|
|
1344
1299
|
title: TITLE6,
|
|
1345
1300
|
description: DESCRIPTION6,
|
|
1346
1301
|
inputSchema: inputSchema6,
|
|
1347
1302
|
annotations: annotations6,
|
|
1348
|
-
handler: (input) => flowLoadHandlerBody(client, input)
|
|
1303
|
+
handler: (input) => flowLoadHandlerBody(client, runtime, input)
|
|
1349
1304
|
};
|
|
1350
1305
|
}
|
|
1351
|
-
async function flowLoadHandlerBody(client, input) {
|
|
1306
|
+
async function flowLoadHandlerBody(client, runtime, input) {
|
|
1352
1307
|
const { source, platform } = input ?? {};
|
|
1353
|
-
if (source &&
|
|
1308
|
+
if (source && isCloudId(source)) {
|
|
1354
1309
|
const resolvedProjectId = resolveDefaultProject(client, void 0);
|
|
1355
1310
|
if (!resolvedProjectId) {
|
|
1356
1311
|
return mcpError7(new Error(NO_DEFAULT_PROJECT_ERROR));
|
|
@@ -1360,9 +1315,8 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
1360
1315
|
flowId: source,
|
|
1361
1316
|
projectId: resolvedProjectId
|
|
1362
1317
|
});
|
|
1363
|
-
const config = flow.config;
|
|
1364
1318
|
return mcpResult7(
|
|
1365
|
-
redactNestedStrings(
|
|
1319
|
+
redactNestedStrings(flowConfigOf(flow), { skip: keepStructural }),
|
|
1366
1320
|
{
|
|
1367
1321
|
next: ["Use flow_validate to check", "Use add-step prompt to modify"]
|
|
1368
1322
|
}
|
|
@@ -1373,7 +1327,7 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
1373
1327
|
}
|
|
1374
1328
|
try {
|
|
1375
1329
|
if (source) {
|
|
1376
|
-
const config = await
|
|
1330
|
+
const config = await runtime.load(source);
|
|
1377
1331
|
return mcpResult7(redactNestedStrings(config, { skip: keepStructural }), {
|
|
1378
1332
|
next: ["Use flow_validate to check", "Use add-step prompt to modify"]
|
|
1379
1333
|
});
|
|
@@ -1393,14 +1347,15 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
1393
1347
|
]
|
|
1394
1348
|
});
|
|
1395
1349
|
} catch (error) {
|
|
1350
|
+
if (error instanceof RuntimeRefusal) return mcpError7(error, error.hint);
|
|
1396
1351
|
const msg = error instanceof Error ? error.message : "";
|
|
1397
1352
|
if (msg.includes("not found") || msg.includes("ENOENT"))
|
|
1398
1353
|
return mcpError7(error, "Check configPath \u2014 expected a flow.json file");
|
|
1399
1354
|
return mcpError7(error);
|
|
1400
1355
|
}
|
|
1401
1356
|
}
|
|
1402
|
-
function registerFlowLoadTool(server, client) {
|
|
1403
|
-
const spec = createFlowLoadToolSpec(client);
|
|
1357
|
+
function registerFlowLoadTool(server, client, runtime) {
|
|
1358
|
+
const spec = createFlowLoadToolSpec(client, runtime);
|
|
1404
1359
|
server.registerTool(
|
|
1405
1360
|
spec.name,
|
|
1406
1361
|
{
|
|
@@ -1466,7 +1421,7 @@ async function feedbackHandlerBody(client, input) {
|
|
|
1466
1421
|
const isAnonymous = explicitAnonymous ?? anonymous ?? true;
|
|
1467
1422
|
await client.submitFeedback(text, {
|
|
1468
1423
|
anonymous: isAnonymous,
|
|
1469
|
-
version: "4.6.
|
|
1424
|
+
version: "4.6.1"
|
|
1470
1425
|
});
|
|
1471
1426
|
return mcpResult8({ ok: true });
|
|
1472
1427
|
} catch (error) {
|
|
@@ -24491,6 +24446,70 @@ async function createMcpEmitter(opts) {
|
|
|
24491
24446
|
};
|
|
24492
24447
|
}
|
|
24493
24448
|
|
|
24449
|
+
// src/runtime/hosted.ts
|
|
24450
|
+
function isHttpUrl(value) {
|
|
24451
|
+
try {
|
|
24452
|
+
const url = new URL(value);
|
|
24453
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
24454
|
+
} catch {
|
|
24455
|
+
return false;
|
|
24456
|
+
}
|
|
24457
|
+
}
|
|
24458
|
+
function looksLikePath(value) {
|
|
24459
|
+
return /[\\/]/.test(value) || value.startsWith(".") || /\.[A-Za-z0-9]+$/.test(value);
|
|
24460
|
+
}
|
|
24461
|
+
function classifyConfigInput(input) {
|
|
24462
|
+
const trimmed = input.trim();
|
|
24463
|
+
if (isHttpUrl(trimmed)) return "url";
|
|
24464
|
+
if (isCloudId(trimmed)) return "cloud-id";
|
|
24465
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "inline-json";
|
|
24466
|
+
if (looksLikePath(trimmed)) return "local-path";
|
|
24467
|
+
return "bare-string";
|
|
24468
|
+
}
|
|
24469
|
+
var HINT_INLINE_OR_ID = "Pass the flow inline as JSON, or reference a saved flow by its flow_ or cfg_ id.";
|
|
24470
|
+
function refuseLocalPath() {
|
|
24471
|
+
return new RuntimeRefusal(
|
|
24472
|
+
"Local file paths are not available on the hosted walkerOS MCP server.",
|
|
24473
|
+
HINT_INLINE_OR_ID
|
|
24474
|
+
);
|
|
24475
|
+
}
|
|
24476
|
+
function refuseUrl() {
|
|
24477
|
+
return new RuntimeRefusal(
|
|
24478
|
+
"Fetching URLs is not available on the hosted walkerOS MCP server.",
|
|
24479
|
+
HINT_INLINE_OR_ID
|
|
24480
|
+
);
|
|
24481
|
+
}
|
|
24482
|
+
function parseInline(trimmed) {
|
|
24483
|
+
try {
|
|
24484
|
+
return JSON.parse(trimmed);
|
|
24485
|
+
} catch (error) {
|
|
24486
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24487
|
+
throw new Error(`Input appears to be JSON but contains errors: ${message}`);
|
|
24488
|
+
}
|
|
24489
|
+
}
|
|
24490
|
+
function createHostedRuntime(client) {
|
|
24491
|
+
return {
|
|
24492
|
+
async load(input) {
|
|
24493
|
+
const trimmed = input.trim();
|
|
24494
|
+
if (trimmed === "") throw new Error("Input is required");
|
|
24495
|
+
switch (classifyConfigInput(trimmed)) {
|
|
24496
|
+
case "url":
|
|
24497
|
+
throw refuseUrl();
|
|
24498
|
+
case "cloud-id":
|
|
24499
|
+
return flowConfigOf(await client.getFlow({ flowId: trimmed }));
|
|
24500
|
+
case "inline-json":
|
|
24501
|
+
return parseInline(trimmed);
|
|
24502
|
+
case "local-path":
|
|
24503
|
+
throw refuseLocalPath();
|
|
24504
|
+
case "bare-string":
|
|
24505
|
+
throw new Error(
|
|
24506
|
+
`Cannot resolve "${trimmed}" on the hosted walkerOS MCP server. ${HINT_INLINE_OR_ID}`
|
|
24507
|
+
);
|
|
24508
|
+
}
|
|
24509
|
+
}
|
|
24510
|
+
};
|
|
24511
|
+
}
|
|
24512
|
+
|
|
24494
24513
|
// src/server.ts
|
|
24495
24514
|
var currentEmitter;
|
|
24496
24515
|
function getMcpEmitterSingleton() {
|
|
@@ -24525,6 +24544,7 @@ function wrapRegisteredToolsWithTelemetry(server) {
|
|
|
24525
24544
|
}
|
|
24526
24545
|
function createWalkerOSMcpServer(opts) {
|
|
24527
24546
|
const packageVersion = opts.version ?? "0.0.0";
|
|
24547
|
+
const runtime = opts.runtime ?? createHostedRuntime(opts.client);
|
|
24528
24548
|
const server = new McpServer(
|
|
24529
24549
|
{
|
|
24530
24550
|
name: "walkeros-flow",
|
|
@@ -24543,12 +24563,12 @@ function createWalkerOSMcpServer(opts) {
|
|
|
24543
24563
|
registerFrameManageTool(server, opts.client);
|
|
24544
24564
|
registerFeedbackTool(server, opts.client);
|
|
24545
24565
|
registerDiagnosticsTool(server, opts.client, packageVersion);
|
|
24546
|
-
registerFlowValidateTool(server);
|
|
24547
|
-
registerFlowBundleTool(server, opts.client);
|
|
24548
|
-
registerFlowSimulateTool(server, opts.client);
|
|
24549
|
-
registerFlowPushTool(server);
|
|
24550
|
-
registerFlowExamplesTool(server);
|
|
24551
|
-
registerFlowLoadTool(server, opts.client);
|
|
24566
|
+
registerFlowValidateTool(server, runtime);
|
|
24567
|
+
registerFlowBundleTool(server, opts.client, runtime);
|
|
24568
|
+
registerFlowSimulateTool(server, opts.client, runtime);
|
|
24569
|
+
registerFlowPushTool(server, runtime);
|
|
24570
|
+
registerFlowExamplesTool(server, runtime);
|
|
24571
|
+
registerFlowLoadTool(server, opts.client, runtime);
|
|
24552
24572
|
registerPackageSearchTool(server);
|
|
24553
24573
|
registerGetPackageSchemaTool(server);
|
|
24554
24574
|
registerPackageSchemaResources(server);
|
|
@@ -24822,8 +24842,155 @@ var HttpToolClient = class {
|
|
|
24822
24842
|
}
|
|
24823
24843
|
};
|
|
24824
24844
|
|
|
24845
|
+
// src/runtime/local.ts
|
|
24846
|
+
import {
|
|
24847
|
+
loadJsonConfig,
|
|
24848
|
+
bundle as bundle2,
|
|
24849
|
+
push,
|
|
24850
|
+
simulateSource,
|
|
24851
|
+
simulateTransformer,
|
|
24852
|
+
simulateCollector,
|
|
24853
|
+
simulateDestination
|
|
24854
|
+
} from "@walkeros/cli";
|
|
24855
|
+
|
|
24856
|
+
// src/runtime/bundle-cache.ts
|
|
24857
|
+
import { createHash } from "crypto";
|
|
24858
|
+
import os from "os";
|
|
24859
|
+
import path from "path";
|
|
24860
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
24861
|
+
import { rmSync } from "fs";
|
|
24862
|
+
import { bundle } from "@walkeros/cli";
|
|
24863
|
+
var MAX_ENTRIES = 8;
|
|
24864
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
24865
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
24866
|
+
var cleanupRegistered = false;
|
|
24867
|
+
function hashConfig(resolvedConfig) {
|
|
24868
|
+
return createHash("sha256").update(resolvedConfig).digest("hex");
|
|
24869
|
+
}
|
|
24870
|
+
function isInlineJsonConfig(resolvedConfig) {
|
|
24871
|
+
const trimmed = resolvedConfig.trimStart();
|
|
24872
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
|
|
24873
|
+
try {
|
|
24874
|
+
JSON.parse(resolvedConfig);
|
|
24875
|
+
return true;
|
|
24876
|
+
} catch {
|
|
24877
|
+
return false;
|
|
24878
|
+
}
|
|
24879
|
+
}
|
|
24880
|
+
function registerProcessCleanup() {
|
|
24881
|
+
if (cleanupRegistered) return;
|
|
24882
|
+
cleanupRegistered = true;
|
|
24883
|
+
const cleanup = () => {
|
|
24884
|
+
for (const entry of cache2.values()) {
|
|
24885
|
+
try {
|
|
24886
|
+
rmSync(entry.dir, { recursive: true, force: true });
|
|
24887
|
+
} catch {
|
|
24888
|
+
}
|
|
24889
|
+
}
|
|
24890
|
+
cache2.clear();
|
|
24891
|
+
};
|
|
24892
|
+
process.once("exit", cleanup);
|
|
24893
|
+
}
|
|
24894
|
+
async function evictIfNeeded() {
|
|
24895
|
+
while (cache2.size > MAX_ENTRIES) {
|
|
24896
|
+
const oldestKey = cache2.keys().next().value;
|
|
24897
|
+
if (oldestKey === void 0) break;
|
|
24898
|
+
const evicted = cache2.get(oldestKey);
|
|
24899
|
+
cache2.delete(oldestKey);
|
|
24900
|
+
if (evicted) await rm(evicted.dir, { recursive: true, force: true });
|
|
24901
|
+
}
|
|
24902
|
+
}
|
|
24903
|
+
async function getOrBuildBundle(resolvedConfig) {
|
|
24904
|
+
if (!isInlineJsonConfig(resolvedConfig)) return void 0;
|
|
24905
|
+
registerProcessCleanup();
|
|
24906
|
+
const key = hashConfig(resolvedConfig);
|
|
24907
|
+
const cached = cache2.get(key);
|
|
24908
|
+
if (cached) {
|
|
24909
|
+
cache2.delete(key);
|
|
24910
|
+
cache2.set(key, cached);
|
|
24911
|
+
return cached.bundlePath;
|
|
24912
|
+
}
|
|
24913
|
+
const pending = inFlight.get(key);
|
|
24914
|
+
if (pending) return pending;
|
|
24915
|
+
const build = (async () => {
|
|
24916
|
+
const dir = path.join(
|
|
24917
|
+
os.tmpdir(),
|
|
24918
|
+
`walkeros-mcp-bundle-${key.slice(0, 16)}`
|
|
24919
|
+
);
|
|
24920
|
+
await rm(dir, { recursive: true, force: true });
|
|
24921
|
+
await mkdir(dir, { recursive: true });
|
|
24922
|
+
const bundlePath = path.join(dir, "flow.mjs");
|
|
24923
|
+
await writeFile(bundlePath, "", "utf-8");
|
|
24924
|
+
await bundle(resolvedConfig, {
|
|
24925
|
+
target: "simulate",
|
|
24926
|
+
silent: true,
|
|
24927
|
+
buildOverrides: { output: bundlePath, format: "esm", minify: false }
|
|
24928
|
+
});
|
|
24929
|
+
cache2.set(key, { bundlePath, dir });
|
|
24930
|
+
await evictIfNeeded();
|
|
24931
|
+
return bundlePath;
|
|
24932
|
+
})();
|
|
24933
|
+
inFlight.set(key, build);
|
|
24934
|
+
try {
|
|
24935
|
+
return await build;
|
|
24936
|
+
} finally {
|
|
24937
|
+
inFlight.delete(key);
|
|
24938
|
+
}
|
|
24939
|
+
}
|
|
24940
|
+
|
|
24941
|
+
// src/runtime/local.ts
|
|
24942
|
+
function createLocalRuntime() {
|
|
24943
|
+
return {
|
|
24944
|
+
load: (input) => loadJsonConfig(input),
|
|
24945
|
+
bundle: (input, opts) => bundle2(input, {
|
|
24946
|
+
flowName: opts.flowName,
|
|
24947
|
+
stats: opts.stats,
|
|
24948
|
+
buildOverrides: opts.output ? { output: opts.output } : void 0
|
|
24949
|
+
}),
|
|
24950
|
+
async simulate(input, opts) {
|
|
24951
|
+
let bundlePath;
|
|
24952
|
+
try {
|
|
24953
|
+
bundlePath = await getOrBuildBundle(input);
|
|
24954
|
+
} catch {
|
|
24955
|
+
bundlePath = void 0;
|
|
24956
|
+
}
|
|
24957
|
+
const common = { bundlePath, flow: opts.flow, silent: true };
|
|
24958
|
+
switch (opts.stepType) {
|
|
24959
|
+
case "source":
|
|
24960
|
+
return simulateSource(input, opts.event, {
|
|
24961
|
+
sourceId: opts.stepId,
|
|
24962
|
+
...common
|
|
24963
|
+
});
|
|
24964
|
+
case "transformer":
|
|
24965
|
+
return simulateTransformer(
|
|
24966
|
+
input,
|
|
24967
|
+
opts.event,
|
|
24968
|
+
{ transformerId: opts.stepId, ...common, ingest: opts.ingest }
|
|
24969
|
+
);
|
|
24970
|
+
case "collector":
|
|
24971
|
+
return simulateCollector(
|
|
24972
|
+
input,
|
|
24973
|
+
opts.event,
|
|
24974
|
+
{ collectorName: opts.stepId, ...common, state: opts.state }
|
|
24975
|
+
);
|
|
24976
|
+
case "destination":
|
|
24977
|
+
return simulateDestination(
|
|
24978
|
+
input,
|
|
24979
|
+
opts.event,
|
|
24980
|
+
{ destinationId: opts.stepId, ...common }
|
|
24981
|
+
);
|
|
24982
|
+
}
|
|
24983
|
+
},
|
|
24984
|
+
push: (input, event, opts) => push(input, event, {
|
|
24985
|
+
json: true,
|
|
24986
|
+
flow: opts.flow,
|
|
24987
|
+
platform: opts.platform
|
|
24988
|
+
})
|
|
24989
|
+
};
|
|
24990
|
+
}
|
|
24991
|
+
|
|
24825
24992
|
// src/stdio.ts
|
|
24826
|
-
setClientContext({ type: "mcp", version: "4.6.
|
|
24993
|
+
setClientContext({ type: "mcp", version: "4.6.1" });
|
|
24827
24994
|
process.on("uncaughtException", (err) => {
|
|
24828
24995
|
const emitter = getMcpEmitterSingleton();
|
|
24829
24996
|
if (emitter) {
|
|
@@ -24843,7 +25010,8 @@ process.on("unhandledRejection", (reason) => {
|
|
|
24843
25010
|
async function main() {
|
|
24844
25011
|
const server = createWalkerOSMcpServer({
|
|
24845
25012
|
client: new HttpToolClient(),
|
|
24846
|
-
version: "4.6.
|
|
25013
|
+
version: "4.6.1",
|
|
25014
|
+
runtime: createLocalRuntime()
|
|
24847
25015
|
});
|
|
24848
25016
|
const transport = new StdioServerTransport();
|
|
24849
25017
|
await server.connect(transport);
|
|
@@ -24853,7 +25021,7 @@ main().catch(async (error) => {
|
|
|
24853
25021
|
try {
|
|
24854
25022
|
const emitter = await createMcpEmitter({
|
|
24855
25023
|
clientInfo: void 0,
|
|
24856
|
-
packageVersion: "4.6.
|
|
25024
|
+
packageVersion: "4.6.1"
|
|
24857
25025
|
});
|
|
24858
25026
|
await emitter.emitError("startup");
|
|
24859
25027
|
} catch {
|