@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/index.js
CHANGED
|
@@ -1,3 +1,98 @@
|
|
|
1
|
+
// src/cloud-flow.ts
|
|
2
|
+
import { isObject } from "@walkeros/core";
|
|
3
|
+
var CLOUD_ID_PATTERN = /^(flow|cfg)_[A-Za-z0-9_-]+$/;
|
|
4
|
+
function isCloudId(input) {
|
|
5
|
+
return CLOUD_ID_PATTERN.test(input);
|
|
6
|
+
}
|
|
7
|
+
function flowConfigOf(flow) {
|
|
8
|
+
return isObject(flow) && isObject(flow.config) ? flow.config : {};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/runtime/types.ts
|
|
12
|
+
var RuntimeRefusal = class extends Error {
|
|
13
|
+
hint;
|
|
14
|
+
constructor(message, hint) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "RuntimeRefusal";
|
|
17
|
+
this.hint = hint;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function refusalHint(error, fallback) {
|
|
21
|
+
return error instanceof RuntimeRefusal ? error.hint : fallback;
|
|
22
|
+
}
|
|
23
|
+
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.";
|
|
24
|
+
function unavailableOperation(operation) {
|
|
25
|
+
const verb = operation === "bundle" ? "Bundling" : operation === "simulate" ? "Simulating" : "Running";
|
|
26
|
+
return new RuntimeRefusal(
|
|
27
|
+
`${verb} a flow is not available on the hosted walkerOS MCP server.`,
|
|
28
|
+
HINT_OUT_OF_PROCESS
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/runtime/hosted.ts
|
|
33
|
+
function isHttpUrl(value) {
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(value);
|
|
36
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function looksLikePath(value) {
|
|
42
|
+
return /[\\/]/.test(value) || value.startsWith(".") || /\.[A-Za-z0-9]+$/.test(value);
|
|
43
|
+
}
|
|
44
|
+
function classifyConfigInput(input) {
|
|
45
|
+
const trimmed = input.trim();
|
|
46
|
+
if (isHttpUrl(trimmed)) return "url";
|
|
47
|
+
if (isCloudId(trimmed)) return "cloud-id";
|
|
48
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "inline-json";
|
|
49
|
+
if (looksLikePath(trimmed)) return "local-path";
|
|
50
|
+
return "bare-string";
|
|
51
|
+
}
|
|
52
|
+
var HINT_INLINE_OR_ID = "Pass the flow inline as JSON, or reference a saved flow by its flow_ or cfg_ id.";
|
|
53
|
+
function refuseLocalPath() {
|
|
54
|
+
return new RuntimeRefusal(
|
|
55
|
+
"Local file paths are not available on the hosted walkerOS MCP server.",
|
|
56
|
+
HINT_INLINE_OR_ID
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
function refuseUrl() {
|
|
60
|
+
return new RuntimeRefusal(
|
|
61
|
+
"Fetching URLs is not available on the hosted walkerOS MCP server.",
|
|
62
|
+
HINT_INLINE_OR_ID
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function parseInline(trimmed) {
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(trimmed);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
+
throw new Error(`Input appears to be JSON but contains errors: ${message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function createHostedRuntime(client) {
|
|
74
|
+
return {
|
|
75
|
+
async load(input) {
|
|
76
|
+
const trimmed = input.trim();
|
|
77
|
+
if (trimmed === "") throw new Error("Input is required");
|
|
78
|
+
switch (classifyConfigInput(trimmed)) {
|
|
79
|
+
case "url":
|
|
80
|
+
throw refuseUrl();
|
|
81
|
+
case "cloud-id":
|
|
82
|
+
return flowConfigOf(await client.getFlow({ flowId: trimmed }));
|
|
83
|
+
case "inline-json":
|
|
84
|
+
return parseInline(trimmed);
|
|
85
|
+
case "local-path":
|
|
86
|
+
throw refuseLocalPath();
|
|
87
|
+
case "bare-string":
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Cannot resolve "${trimmed}" on the hosted walkerOS MCP server. ${HINT_INLINE_OR_ID}`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
1
96
|
// src/tools/auth.ts
|
|
2
97
|
import { z } from "zod";
|
|
3
98
|
import { mcpResult, mcpError } from "@walkeros/core";
|
|
@@ -2738,7 +2833,7 @@ async function feedbackHandlerBody(client, input) {
|
|
|
2738
2833
|
const isAnonymous = explicitAnonymous ?? anonymous ?? true;
|
|
2739
2834
|
await client.submitFeedback(text, {
|
|
2740
2835
|
anonymous: isAnonymous,
|
|
2741
|
-
version: "4.6.
|
|
2836
|
+
version: "4.6.1"
|
|
2742
2837
|
});
|
|
2743
2838
|
return mcpResult10({ ok: true });
|
|
2744
2839
|
} catch (error) {
|
|
@@ -2762,7 +2857,7 @@ function registerFeedbackTool(server, client) {
|
|
|
2762
2857
|
}
|
|
2763
2858
|
|
|
2764
2859
|
// src/tools/validate.ts
|
|
2765
|
-
import { validate
|
|
2860
|
+
import { validate } from "@walkeros/cli";
|
|
2766
2861
|
import { schemas } from "@walkeros/cli/dev";
|
|
2767
2862
|
import { mcpResult as mcpResult11, mcpError as mcpError11 } from "@walkeros/core";
|
|
2768
2863
|
|
|
@@ -2857,6 +2952,22 @@ var ExamplesListOutputShape = {
|
|
|
2857
2952
|
};
|
|
2858
2953
|
|
|
2859
2954
|
// src/tools/validate.ts
|
|
2955
|
+
async function loadValidateInput(runtime, input, type) {
|
|
2956
|
+
if (!input || input.trim() === "") throw new Error(`${type} is required`);
|
|
2957
|
+
const trimmed = input.trim();
|
|
2958
|
+
const looksLikeJson = trimmed.startsWith("{") || trimmed.startsWith("[");
|
|
2959
|
+
try {
|
|
2960
|
+
return await runtime.load(input);
|
|
2961
|
+
} catch (error) {
|
|
2962
|
+
if (error instanceof RuntimeRefusal || isCloudId(trimmed)) throw error;
|
|
2963
|
+
if (looksLikeJson) {
|
|
2964
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2965
|
+
throw new Error(`Failed to parse ${type}. ${message}`);
|
|
2966
|
+
}
|
|
2967
|
+
if (type === "event") return { name: trimmed };
|
|
2968
|
+
throw error;
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2860
2971
|
var DEPRECATED_STORE_PACKAGE = "@walkeros/store-memory";
|
|
2861
2972
|
function detectDeprecatedStorePackages(config) {
|
|
2862
2973
|
const errors = [];
|
|
@@ -2886,7 +2997,7 @@ function detectDeprecatedStorePackages(config) {
|
|
|
2886
2997
|
return errors;
|
|
2887
2998
|
}
|
|
2888
2999
|
var TITLE11 = "Validate Flow";
|
|
2889
|
-
var DESCRIPTION9 = "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.";
|
|
3000
|
+
var DESCRIPTION9 = "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.";
|
|
2890
3001
|
var inputSchema9 = schemas.ValidateInputShape;
|
|
2891
3002
|
var annotations11 = {
|
|
2892
3003
|
readOnlyHint: true,
|
|
@@ -2894,17 +3005,17 @@ var annotations11 = {
|
|
|
2894
3005
|
idempotentHint: true,
|
|
2895
3006
|
openWorldHint: false
|
|
2896
3007
|
};
|
|
2897
|
-
function createFlowValidateToolSpec() {
|
|
3008
|
+
function createFlowValidateToolSpec(runtime) {
|
|
2898
3009
|
return {
|
|
2899
3010
|
name: "flow_validate",
|
|
2900
3011
|
title: TITLE11,
|
|
2901
3012
|
description: DESCRIPTION9,
|
|
2902
3013
|
inputSchema: inputSchema9,
|
|
2903
3014
|
annotations: annotations11,
|
|
2904
|
-
handler: (input) => flowValidateHandlerBody(input)
|
|
3015
|
+
handler: (input) => flowValidateHandlerBody(runtime, input)
|
|
2905
3016
|
};
|
|
2906
3017
|
}
|
|
2907
|
-
async function flowValidateHandlerBody(input) {
|
|
3018
|
+
async function flowValidateHandlerBody(runtime, input) {
|
|
2908
3019
|
const {
|
|
2909
3020
|
type,
|
|
2910
3021
|
input: validateInput,
|
|
@@ -2912,30 +3023,27 @@ async function flowValidateHandlerBody(input) {
|
|
|
2912
3023
|
path: path2
|
|
2913
3024
|
} = input ?? {};
|
|
2914
3025
|
try {
|
|
2915
|
-
const
|
|
3026
|
+
const resolved = await loadValidateInput(runtime, validateInput, type);
|
|
3027
|
+
const result = await validate(type, resolved, {
|
|
2916
3028
|
flow,
|
|
2917
3029
|
path: path2
|
|
2918
3030
|
});
|
|
2919
3031
|
let augmented = result;
|
|
2920
|
-
if (type === "flow"
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
errors: [...result.errors, ...deprecatedErrors]
|
|
2929
|
-
};
|
|
2930
|
-
}
|
|
2931
|
-
} catch {
|
|
3032
|
+
if (type === "flow") {
|
|
3033
|
+
const deprecatedErrors = detectDeprecatedStorePackages(resolved);
|
|
3034
|
+
if (deprecatedErrors.length > 0) {
|
|
3035
|
+
augmented = {
|
|
3036
|
+
...result,
|
|
3037
|
+
valid: false,
|
|
3038
|
+
errors: [...result.errors, ...deprecatedErrors]
|
|
3039
|
+
};
|
|
2932
3040
|
}
|
|
2933
3041
|
}
|
|
2934
3042
|
const hints = augmented.valid ? {
|
|
2935
|
-
next: [
|
|
3043
|
+
next: runtime.simulate ? [
|
|
2936
3044
|
"Use flow_simulate to test event flow",
|
|
2937
3045
|
"Use flow_bundle to build"
|
|
2938
|
-
]
|
|
3046
|
+
] : [HINT_OUT_OF_PROCESS]
|
|
2939
3047
|
} : {
|
|
2940
3048
|
next: [
|
|
2941
3049
|
"Fix errors above, then run flow_validate again",
|
|
@@ -2946,12 +3054,15 @@ async function flowValidateHandlerBody(input) {
|
|
|
2946
3054
|
} catch (error) {
|
|
2947
3055
|
return mcpError11(
|
|
2948
3056
|
error,
|
|
2949
|
-
|
|
3057
|
+
refusalHint(
|
|
3058
|
+
error,
|
|
3059
|
+
"Check the input parameter \u2014 expected a JSON string, file path, or URL"
|
|
3060
|
+
)
|
|
2950
3061
|
);
|
|
2951
3062
|
}
|
|
2952
3063
|
}
|
|
2953
|
-
function registerFlowValidateTool(server) {
|
|
2954
|
-
const spec = createFlowValidateToolSpec();
|
|
3064
|
+
function registerFlowValidateTool(server, runtime) {
|
|
3065
|
+
const spec = createFlowValidateToolSpec(runtime);
|
|
2955
3066
|
server.registerTool(
|
|
2956
3067
|
spec.name,
|
|
2957
3068
|
{
|
|
@@ -2969,17 +3080,14 @@ function registerFlowValidateTool(server) {
|
|
|
2969
3080
|
}
|
|
2970
3081
|
|
|
2971
3082
|
// src/tools/bundle.ts
|
|
2972
|
-
import { bundle } from "@walkeros/cli";
|
|
2973
3083
|
import { schemas as schemas2 } from "@walkeros/cli/dev";
|
|
2974
3084
|
import { mcpResult as mcpResult12, mcpError as mcpError12 } from "@walkeros/core";
|
|
2975
3085
|
|
|
2976
3086
|
// src/tools/resolve-config-path.ts
|
|
2977
|
-
var API_ID_PREFIX = /^(flow|cfg)_/;
|
|
2978
3087
|
async function resolveConfigPath(client, configPath) {
|
|
2979
|
-
if (!
|
|
3088
|
+
if (!isCloudId(configPath)) return configPath;
|
|
2980
3089
|
const flow = await client.getFlow({ flowId: configPath });
|
|
2981
|
-
|
|
2982
|
-
return JSON.stringify(config ?? {});
|
|
3090
|
+
return JSON.stringify(flowConfigOf(flow));
|
|
2983
3091
|
}
|
|
2984
3092
|
|
|
2985
3093
|
// src/tools/bundle.ts
|
|
@@ -2994,24 +3102,28 @@ var annotations12 = {
|
|
|
2994
3102
|
idempotentHint: false,
|
|
2995
3103
|
openWorldHint: true
|
|
2996
3104
|
};
|
|
2997
|
-
function createFlowBundleToolSpec(client) {
|
|
3105
|
+
function createFlowBundleToolSpec(client, runtime) {
|
|
2998
3106
|
return {
|
|
2999
3107
|
name: "flow_bundle",
|
|
3000
3108
|
title: TITLE12,
|
|
3001
3109
|
description: DESCRIPTION10,
|
|
3002
3110
|
inputSchema: inputSchema10,
|
|
3003
3111
|
annotations: annotations12,
|
|
3004
|
-
handler: (input) => flowBundleHandlerBody(client, input)
|
|
3112
|
+
handler: (input) => flowBundleHandlerBody(client, runtime, input)
|
|
3005
3113
|
};
|
|
3006
3114
|
}
|
|
3007
|
-
async function flowBundleHandlerBody(client, input) {
|
|
3115
|
+
async function flowBundleHandlerBody(client, runtime, input) {
|
|
3008
3116
|
const { configPath, flow, stats, output } = input ?? {};
|
|
3117
|
+
if (!runtime.bundle) {
|
|
3118
|
+
const refusal = unavailableOperation("bundle");
|
|
3119
|
+
return mcpError12(refusal, refusal.hint);
|
|
3120
|
+
}
|
|
3009
3121
|
try {
|
|
3010
3122
|
const resolvedConfigPath = await resolveConfigPath(client, configPath);
|
|
3011
|
-
const result = await bundle(resolvedConfigPath, {
|
|
3123
|
+
const result = await runtime.bundle(resolvedConfigPath, {
|
|
3012
3124
|
flowName: flow,
|
|
3013
3125
|
stats: stats ?? true,
|
|
3014
|
-
|
|
3126
|
+
output
|
|
3015
3127
|
});
|
|
3016
3128
|
if (!result) {
|
|
3017
3129
|
return mcpResult12(
|
|
@@ -3035,11 +3147,14 @@ async function flowBundleHandlerBody(client, input) {
|
|
|
3035
3147
|
}
|
|
3036
3148
|
);
|
|
3037
3149
|
} catch (error) {
|
|
3038
|
-
return mcpError12(
|
|
3150
|
+
return mcpError12(
|
|
3151
|
+
error,
|
|
3152
|
+
refusalHint(error, "Run flow_validate for detailed error messages")
|
|
3153
|
+
);
|
|
3039
3154
|
}
|
|
3040
3155
|
}
|
|
3041
|
-
function registerFlowBundleTool(server, client) {
|
|
3042
|
-
const spec = createFlowBundleToolSpec(client);
|
|
3156
|
+
function registerFlowBundleTool(server, client, runtime) {
|
|
3157
|
+
const spec = createFlowBundleToolSpec(client, runtime);
|
|
3043
3158
|
server.registerTool(
|
|
3044
3159
|
spec.name,
|
|
3045
3160
|
{
|
|
@@ -3058,101 +3173,17 @@ function registerFlowBundleTool(server, client) {
|
|
|
3058
3173
|
|
|
3059
3174
|
// src/tools/simulate.ts
|
|
3060
3175
|
import { z as z12 } from "zod";
|
|
3061
|
-
import {
|
|
3062
|
-
simulateSource,
|
|
3063
|
-
simulateTransformer,
|
|
3064
|
-
simulateDestination,
|
|
3065
|
-
simulateCollector
|
|
3066
|
-
} from "@walkeros/cli";
|
|
3067
3176
|
import { schemas as schemas3 } from "@walkeros/cli/dev";
|
|
3068
3177
|
import { mcpResult as mcpResult13, mcpError as mcpError13 } from "@walkeros/core";
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
var MAX_ENTRIES = 8;
|
|
3078
|
-
var cache = /* @__PURE__ */ new Map();
|
|
3079
|
-
var inFlight = /* @__PURE__ */ new Map();
|
|
3080
|
-
var cleanupRegistered = false;
|
|
3081
|
-
function hashConfig(resolvedConfig) {
|
|
3082
|
-
return createHash("sha256").update(resolvedConfig).digest("hex");
|
|
3083
|
-
}
|
|
3084
|
-
function isInlineJsonConfig(resolvedConfig) {
|
|
3085
|
-
const trimmed = resolvedConfig.trimStart();
|
|
3086
|
-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
|
|
3087
|
-
try {
|
|
3088
|
-
JSON.parse(resolvedConfig);
|
|
3089
|
-
return true;
|
|
3090
|
-
} catch {
|
|
3091
|
-
return false;
|
|
3092
|
-
}
|
|
3093
|
-
}
|
|
3094
|
-
function registerProcessCleanup() {
|
|
3095
|
-
if (cleanupRegistered) return;
|
|
3096
|
-
cleanupRegistered = true;
|
|
3097
|
-
const cleanup = () => {
|
|
3098
|
-
for (const entry of cache.values()) {
|
|
3099
|
-
try {
|
|
3100
|
-
rmSync(entry.dir, { recursive: true, force: true });
|
|
3101
|
-
} catch {
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
cache.clear();
|
|
3105
|
-
};
|
|
3106
|
-
process.once("exit", cleanup);
|
|
3107
|
-
}
|
|
3108
|
-
async function evictIfNeeded() {
|
|
3109
|
-
while (cache.size > MAX_ENTRIES) {
|
|
3110
|
-
const oldestKey = cache.keys().next().value;
|
|
3111
|
-
if (oldestKey === void 0) break;
|
|
3112
|
-
const evicted = cache.get(oldestKey);
|
|
3113
|
-
cache.delete(oldestKey);
|
|
3114
|
-
if (evicted) await rm(evicted.dir, { recursive: true, force: true });
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
async function getOrBuildBundle(resolvedConfig) {
|
|
3118
|
-
if (!isInlineJsonConfig(resolvedConfig)) return void 0;
|
|
3119
|
-
registerProcessCleanup();
|
|
3120
|
-
const key = hashConfig(resolvedConfig);
|
|
3121
|
-
const cached = cache.get(key);
|
|
3122
|
-
if (cached) {
|
|
3123
|
-
cache.delete(key);
|
|
3124
|
-
cache.set(key, cached);
|
|
3125
|
-
return cached.bundlePath;
|
|
3126
|
-
}
|
|
3127
|
-
const pending = inFlight.get(key);
|
|
3128
|
-
if (pending) return pending;
|
|
3129
|
-
const build = (async () => {
|
|
3130
|
-
const dir = path.join(
|
|
3131
|
-
os.tmpdir(),
|
|
3132
|
-
`walkeros-mcp-bundle-${key.slice(0, 16)}`
|
|
3133
|
-
);
|
|
3134
|
-
await rm(dir, { recursive: true, force: true });
|
|
3135
|
-
await mkdir(dir, { recursive: true });
|
|
3136
|
-
const bundlePath = path.join(dir, "flow.mjs");
|
|
3137
|
-
await writeFile(bundlePath, "", "utf-8");
|
|
3138
|
-
await bundle2(resolvedConfig, {
|
|
3139
|
-
target: "simulate",
|
|
3140
|
-
silent: true,
|
|
3141
|
-
buildOverrides: { output: bundlePath, format: "esm", minify: false }
|
|
3142
|
-
});
|
|
3143
|
-
cache.set(key, { bundlePath, dir });
|
|
3144
|
-
await evictIfNeeded();
|
|
3145
|
-
return bundlePath;
|
|
3146
|
-
})();
|
|
3147
|
-
inFlight.set(key, build);
|
|
3148
|
-
try {
|
|
3149
|
-
return await build;
|
|
3150
|
-
} finally {
|
|
3151
|
-
inFlight.delete(key);
|
|
3152
|
-
}
|
|
3178
|
+
var STEP_TYPES = [
|
|
3179
|
+
"source",
|
|
3180
|
+
"transformer",
|
|
3181
|
+
"collector",
|
|
3182
|
+
"destination"
|
|
3183
|
+
];
|
|
3184
|
+
function isStepType(value) {
|
|
3185
|
+
return STEP_TYPES.some((t) => t === value);
|
|
3153
3186
|
}
|
|
3154
|
-
|
|
3155
|
-
// src/tools/simulate.ts
|
|
3156
3187
|
var TITLE13 = "Simulate Flow";
|
|
3157
3188
|
var DESCRIPTION11 = '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.';
|
|
3158
3189
|
var inputSchema11 = {
|
|
@@ -3181,23 +3212,31 @@ var inputSchema11 = {
|
|
|
3181
3212
|
)
|
|
3182
3213
|
};
|
|
3183
3214
|
var annotations13 = {
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3215
|
+
// Simulation downloads the packages a config names and runs caller-controlled
|
|
3216
|
+
// flow code in process. Destinations are mocked, but that code is not, so the
|
|
3217
|
+
// hints stay conservative: side effects possible, repeat calls not assumed
|
|
3218
|
+
// safe, external systems reachable.
|
|
3219
|
+
readOnlyHint: false,
|
|
3220
|
+
destructiveHint: true,
|
|
3221
|
+
idempotentHint: false,
|
|
3222
|
+
openWorldHint: true
|
|
3188
3223
|
};
|
|
3189
|
-
function createFlowSimulateToolSpec(client) {
|
|
3224
|
+
function createFlowSimulateToolSpec(client, runtime) {
|
|
3190
3225
|
return {
|
|
3191
3226
|
name: "flow_simulate",
|
|
3192
3227
|
title: TITLE13,
|
|
3193
3228
|
description: DESCRIPTION11,
|
|
3194
3229
|
inputSchema: inputSchema11,
|
|
3195
3230
|
annotations: annotations13,
|
|
3196
|
-
handler: (input) => flowSimulateHandlerBody(client, input)
|
|
3231
|
+
handler: (input) => flowSimulateHandlerBody(client, runtime, input)
|
|
3197
3232
|
};
|
|
3198
3233
|
}
|
|
3199
|
-
async function flowSimulateHandlerBody(client, input) {
|
|
3234
|
+
async function flowSimulateHandlerBody(client, runtime, input) {
|
|
3200
3235
|
const { configPath, event, flow, platform, step, verbose, ingest, state } = input ?? {};
|
|
3236
|
+
if (!runtime.simulate) {
|
|
3237
|
+
const refusal = unavailableOperation("simulate");
|
|
3238
|
+
return mcpError13(refusal, refusal.hint);
|
|
3239
|
+
}
|
|
3201
3240
|
try {
|
|
3202
3241
|
if (!event) {
|
|
3203
3242
|
throw new Error(
|
|
@@ -3227,66 +3266,16 @@ async function flowSimulateHandlerBody(client, input) {
|
|
|
3227
3266
|
}
|
|
3228
3267
|
const stepType = step.substring(0, dotIndex);
|
|
3229
3268
|
const stepId = step.substring(dotIndex + 1);
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
} catch {
|
|
3235
|
-
bundlePath = void 0;
|
|
3236
|
-
}
|
|
3237
|
-
let result;
|
|
3238
|
-
switch (stepType) {
|
|
3239
|
-
case "source":
|
|
3240
|
-
result = await simulateSource(resolvedConfigPath, resolvedEvent, {
|
|
3241
|
-
sourceId: stepId,
|
|
3242
|
-
bundlePath,
|
|
3243
|
-
flow,
|
|
3244
|
-
silent: true
|
|
3245
|
-
});
|
|
3246
|
-
break;
|
|
3247
|
-
case "transformer":
|
|
3248
|
-
result = await simulateTransformer(
|
|
3249
|
-
resolvedConfigPath,
|
|
3250
|
-
resolvedEvent,
|
|
3251
|
-
{
|
|
3252
|
-
transformerId: stepId,
|
|
3253
|
-
bundlePath,
|
|
3254
|
-
flow,
|
|
3255
|
-
silent: true,
|
|
3256
|
-
ingest
|
|
3257
|
-
}
|
|
3258
|
-
);
|
|
3259
|
-
break;
|
|
3260
|
-
case "collector":
|
|
3261
|
-
result = await simulateCollector(
|
|
3262
|
-
resolvedConfigPath,
|
|
3263
|
-
resolvedEvent,
|
|
3264
|
-
{
|
|
3265
|
-
collectorName: stepId,
|
|
3266
|
-
bundlePath,
|
|
3267
|
-
flow,
|
|
3268
|
-
silent: true,
|
|
3269
|
-
state
|
|
3270
|
-
}
|
|
3271
|
-
);
|
|
3272
|
-
break;
|
|
3273
|
-
case "destination":
|
|
3274
|
-
result = await simulateDestination(
|
|
3275
|
-
resolvedConfigPath,
|
|
3276
|
-
resolvedEvent,
|
|
3277
|
-
{
|
|
3278
|
-
destinationId: stepId,
|
|
3279
|
-
bundlePath,
|
|
3280
|
-
flow,
|
|
3281
|
-
silent: true
|
|
3282
|
-
}
|
|
3283
|
-
);
|
|
3284
|
-
break;
|
|
3285
|
-
default:
|
|
3286
|
-
throw new Error(
|
|
3287
|
-
`Unknown step type "${stepType}". Use "source", "collector", "transformer", or "destination".`
|
|
3288
|
-
);
|
|
3269
|
+
if (!isStepType(stepType)) {
|
|
3270
|
+
throw new Error(
|
|
3271
|
+
`Unknown step type "${stepType}". Use "source", "collector", "transformer", or "destination".`
|
|
3272
|
+
);
|
|
3289
3273
|
}
|
|
3274
|
+
const resolvedConfigPath = await resolveConfigPath(client, configPath);
|
|
3275
|
+
const result = await runtime.simulate(
|
|
3276
|
+
resolvedConfigPath,
|
|
3277
|
+
{ stepType, stepId, event: resolvedEvent, flow, ingest, state }
|
|
3278
|
+
);
|
|
3290
3279
|
const success = !result.error;
|
|
3291
3280
|
const errorMessage = result.error?.message;
|
|
3292
3281
|
if (result.step === "source") {
|
|
@@ -3374,11 +3363,11 @@ async function flowSimulateHandlerBody(client, input) {
|
|
|
3374
3363
|
if (msg.includes("not found in collector")) {
|
|
3375
3364
|
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.';
|
|
3376
3365
|
}
|
|
3377
|
-
return mcpError13(error, hint);
|
|
3366
|
+
return mcpError13(error, refusalHint(error, hint));
|
|
3378
3367
|
}
|
|
3379
3368
|
}
|
|
3380
|
-
function registerFlowSimulateTool(server, client) {
|
|
3381
|
-
const spec = createFlowSimulateToolSpec(client);
|
|
3369
|
+
function registerFlowSimulateTool(server, client, runtime) {
|
|
3370
|
+
const spec = createFlowSimulateToolSpec(client, runtime);
|
|
3382
3371
|
server.registerTool(
|
|
3383
3372
|
spec.name,
|
|
3384
3373
|
{
|
|
@@ -3397,7 +3386,6 @@ function registerFlowSimulateTool(server, client) {
|
|
|
3397
3386
|
|
|
3398
3387
|
// src/tools/push.ts
|
|
3399
3388
|
import { z as z13 } from "zod";
|
|
3400
|
-
import { push } from "@walkeros/cli";
|
|
3401
3389
|
import { schemas as schemas4 } from "@walkeros/cli/dev";
|
|
3402
3390
|
import { mcpResult as mcpResult14, mcpError as mcpError14 } from "@walkeros/core";
|
|
3403
3391
|
var TITLE14 = "Push Events";
|
|
@@ -3416,21 +3404,24 @@ var annotations14 = {
|
|
|
3416
3404
|
idempotentHint: false,
|
|
3417
3405
|
openWorldHint: true
|
|
3418
3406
|
};
|
|
3419
|
-
function createFlowPushToolSpec() {
|
|
3407
|
+
function createFlowPushToolSpec(runtime) {
|
|
3420
3408
|
return {
|
|
3421
3409
|
name: "flow_push",
|
|
3422
3410
|
title: TITLE14,
|
|
3423
3411
|
description: DESCRIPTION12,
|
|
3424
3412
|
inputSchema: inputSchema12,
|
|
3425
3413
|
annotations: annotations14,
|
|
3426
|
-
handler: (input) => flowPushHandlerBody(input)
|
|
3414
|
+
handler: (input) => flowPushHandlerBody(runtime, input)
|
|
3427
3415
|
};
|
|
3428
3416
|
}
|
|
3429
|
-
async function flowPushHandlerBody(input) {
|
|
3417
|
+
async function flowPushHandlerBody(runtime, input) {
|
|
3430
3418
|
const { configPath, event, flow, platform } = input ?? {};
|
|
3419
|
+
if (!runtime.push) {
|
|
3420
|
+
const refusal = unavailableOperation("push");
|
|
3421
|
+
return mcpError14(refusal, refusal.hint);
|
|
3422
|
+
}
|
|
3431
3423
|
try {
|
|
3432
|
-
const result = await push(configPath, event, {
|
|
3433
|
-
json: true,
|
|
3424
|
+
const result = await runtime.push(configPath, event, {
|
|
3434
3425
|
flow,
|
|
3435
3426
|
platform
|
|
3436
3427
|
});
|
|
@@ -3444,12 +3435,15 @@ async function flowPushHandlerBody(input) {
|
|
|
3444
3435
|
} catch (error) {
|
|
3445
3436
|
return mcpError14(
|
|
3446
3437
|
error,
|
|
3447
|
-
|
|
3438
|
+
refusalHint(
|
|
3439
|
+
error,
|
|
3440
|
+
"Check configPath and event format. For web flows, use flow_simulate."
|
|
3441
|
+
)
|
|
3448
3442
|
);
|
|
3449
3443
|
}
|
|
3450
3444
|
}
|
|
3451
|
-
function registerFlowPushTool(server) {
|
|
3452
|
-
const spec = createFlowPushToolSpec();
|
|
3445
|
+
function registerFlowPushTool(server, runtime) {
|
|
3446
|
+
const spec = createFlowPushToolSpec(runtime);
|
|
3453
3447
|
server.registerTool(
|
|
3454
3448
|
spec.name,
|
|
3455
3449
|
{
|
|
@@ -3468,7 +3462,6 @@ function registerFlowPushTool(server) {
|
|
|
3468
3462
|
|
|
3469
3463
|
// src/tools/examples.ts
|
|
3470
3464
|
import { z as z14 } from "zod";
|
|
3471
|
-
import { loadJsonConfig as loadJsonConfig2 } from "@walkeros/cli";
|
|
3472
3465
|
import { fetchPackage, mcpResult as mcpResult15, mcpError as mcpError15 } from "@walkeros/core";
|
|
3473
3466
|
|
|
3474
3467
|
// src/catalog.ts
|
|
@@ -3476,7 +3469,7 @@ var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
|
|
|
3476
3469
|
var JSDELIVR_BASE = "https://cdn.jsdelivr.net/npm";
|
|
3477
3470
|
var WALKEROS_JSON_PATH = "dist/walkerOS.json";
|
|
3478
3471
|
var CACHE_TTL = 5 * 60 * 1e3;
|
|
3479
|
-
var CLIENT_HEADER = "walkeros-mcp/4.6.
|
|
3472
|
+
var CLIENT_HEADER = "walkeros-mcp/4.6.1";
|
|
3480
3473
|
function getPackageBaseUrl() {
|
|
3481
3474
|
return process.env.WALKEROS_APP_URL || void 0;
|
|
3482
3475
|
}
|
|
@@ -3484,7 +3477,7 @@ var lastFetchInfo;
|
|
|
3484
3477
|
function getLastCatalogSource() {
|
|
3485
3478
|
return lastFetchInfo;
|
|
3486
3479
|
}
|
|
3487
|
-
var
|
|
3480
|
+
var cache = /* @__PURE__ */ new Map();
|
|
3488
3481
|
function normalizePlatform(platform) {
|
|
3489
3482
|
if (platform == null) return [];
|
|
3490
3483
|
if (typeof platform === "string") {
|
|
@@ -3498,7 +3491,7 @@ function normalizePlatform(platform) {
|
|
|
3498
3491
|
async function fetchCatalog(filters) {
|
|
3499
3492
|
const sourceKey = filters?.baseUrl ?? "npm";
|
|
3500
3493
|
const warnings = [];
|
|
3501
|
-
const cached =
|
|
3494
|
+
const cached = cache.get(sourceKey);
|
|
3502
3495
|
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
|
|
3503
3496
|
lastFetchInfo = {
|
|
3504
3497
|
source: filters?.baseUrl ? "app" : "npm",
|
|
@@ -3556,7 +3549,7 @@ async function fetchCatalog(filters) {
|
|
|
3556
3549
|
timestamp: Date.now()
|
|
3557
3550
|
};
|
|
3558
3551
|
if (result.entries.length > 0 && result.complete) {
|
|
3559
|
-
|
|
3552
|
+
cache.set(sourceKey, { entries: result.entries, timestamp: Date.now() });
|
|
3560
3553
|
}
|
|
3561
3554
|
return { entries: applyFilters(result.entries, filters), warnings };
|
|
3562
3555
|
}
|
|
@@ -3629,10 +3622,13 @@ function applyFilters(entries, filters) {
|
|
|
3629
3622
|
}
|
|
3630
3623
|
|
|
3631
3624
|
// src/tools/examples.ts
|
|
3625
|
+
var MAX_PACKAGE_LOOKUPS = 25;
|
|
3632
3626
|
var TITLE15 = "Flow Examples";
|
|
3633
|
-
var DESCRIPTION13 = '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.';
|
|
3627
|
+
var DESCRIPTION13 = '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.';
|
|
3634
3628
|
var inputSchema13 = {
|
|
3635
|
-
configPath: z14.string().min(1).describe(
|
|
3629
|
+
configPath: z14.string().min(1).describe(
|
|
3630
|
+
"Inline JSON string, file path, or URL of a flow configuration (hosted server: inline JSON or a saved flow id only)"
|
|
3631
|
+
),
|
|
3636
3632
|
flow: z14.string().optional().describe("Flow name for multi-flow configs"),
|
|
3637
3633
|
step: z14.string().optional().describe('Filter to a specific step (e.g., "destination.gtag")'),
|
|
3638
3634
|
full: z14.boolean().optional().describe(
|
|
@@ -3648,20 +3644,20 @@ var annotations15 = {
|
|
|
3648
3644
|
idempotentHint: true,
|
|
3649
3645
|
openWorldHint: false
|
|
3650
3646
|
};
|
|
3651
|
-
function createFlowExamplesToolSpec() {
|
|
3647
|
+
function createFlowExamplesToolSpec(runtime) {
|
|
3652
3648
|
return {
|
|
3653
3649
|
name: "flow_examples",
|
|
3654
3650
|
title: TITLE15,
|
|
3655
3651
|
description: DESCRIPTION13,
|
|
3656
3652
|
inputSchema: inputSchema13,
|
|
3657
3653
|
annotations: annotations15,
|
|
3658
|
-
handler: (input) => flowExamplesHandlerBody(input)
|
|
3654
|
+
handler: (input) => flowExamplesHandlerBody(runtime, input)
|
|
3659
3655
|
};
|
|
3660
3656
|
}
|
|
3661
|
-
async function flowExamplesHandlerBody(input) {
|
|
3657
|
+
async function flowExamplesHandlerBody(runtime, input) {
|
|
3662
3658
|
const { configPath, flow, step, full, includeHidden } = input ?? {};
|
|
3663
3659
|
try {
|
|
3664
|
-
const rawConfig = await
|
|
3660
|
+
const rawConfig = await runtime.load(configPath);
|
|
3665
3661
|
const flowNames = Object.keys(rawConfig.flows || {});
|
|
3666
3662
|
const flowName = flow || (flowNames.length === 1 ? flowNames[0] : void 0);
|
|
3667
3663
|
if (!flowName) {
|
|
@@ -3715,6 +3711,19 @@ async function flowExamplesHandlerBody(input) {
|
|
|
3715
3711
|
}
|
|
3716
3712
|
return void 0;
|
|
3717
3713
|
};
|
|
3714
|
+
const packageLookups = /* @__PURE__ */ new Map();
|
|
3715
|
+
let skippedPackages = false;
|
|
3716
|
+
const examplesForPackage = (packageName) => {
|
|
3717
|
+
const existing = packageLookups.get(packageName);
|
|
3718
|
+
if (existing) return existing;
|
|
3719
|
+
if (packageLookups.size >= MAX_PACKAGE_LOOKUPS) {
|
|
3720
|
+
skippedPackages = true;
|
|
3721
|
+
return Promise.resolve(void 0);
|
|
3722
|
+
}
|
|
3723
|
+
const lookup = loadPackageExamples(packageName);
|
|
3724
|
+
packageLookups.set(packageName, lookup);
|
|
3725
|
+
return lookup;
|
|
3726
|
+
};
|
|
3718
3727
|
const stepTypes = [
|
|
3719
3728
|
{ key: "sources", type: "source" },
|
|
3720
3729
|
{ key: "transformers", type: "transformer" },
|
|
@@ -3731,7 +3740,7 @@ async function flowExamplesHandlerBody(input) {
|
|
|
3731
3740
|
continue;
|
|
3732
3741
|
}
|
|
3733
3742
|
if (!ref.package) continue;
|
|
3734
|
-
const packageExamples = await
|
|
3743
|
+
const packageExamples = await examplesForPackage(ref.package);
|
|
3735
3744
|
if (packageExamples)
|
|
3736
3745
|
examples.push(...toItems(packageExamples, type, name, "package"));
|
|
3737
3746
|
}
|
|
@@ -3741,21 +3750,33 @@ async function flowExamplesHandlerBody(input) {
|
|
|
3741
3750
|
count: examples.length,
|
|
3742
3751
|
examples
|
|
3743
3752
|
};
|
|
3744
|
-
const
|
|
3745
|
-
next: ["Use flow_simulate with step and event to simulate"]
|
|
3746
|
-
};
|
|
3753
|
+
const warnings = [];
|
|
3747
3754
|
if (examples.length === 0) {
|
|
3748
|
-
|
|
3755
|
+
warnings.push(
|
|
3749
3756
|
"No examples found. Add examples to step entries, or reference a package that ships examples (see package_get)."
|
|
3750
|
-
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
if (skippedPackages) {
|
|
3760
|
+
warnings.push(
|
|
3761
|
+
`Package examples were looked up for the first ${MAX_PACKAGE_LOOKUPS} packages only. Use step to narrow the result.`
|
|
3762
|
+
);
|
|
3751
3763
|
}
|
|
3764
|
+
const hints = {
|
|
3765
|
+
next: [
|
|
3766
|
+
runtime.simulate ? "Use flow_simulate with step and event to simulate" : HINT_OUT_OF_PROCESS
|
|
3767
|
+
],
|
|
3768
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
3769
|
+
};
|
|
3752
3770
|
return mcpResult15(result, hints);
|
|
3753
3771
|
} catch (error) {
|
|
3754
|
-
return mcpError15(
|
|
3772
|
+
return mcpError15(
|
|
3773
|
+
error,
|
|
3774
|
+
refusalHint(error, "Check configPath \u2014 expected a flow.json file")
|
|
3775
|
+
);
|
|
3755
3776
|
}
|
|
3756
3777
|
}
|
|
3757
|
-
function registerFlowExamplesTool(server) {
|
|
3758
|
-
const spec = createFlowExamplesToolSpec();
|
|
3778
|
+
function registerFlowExamplesTool(server, runtime) {
|
|
3779
|
+
const spec = createFlowExamplesToolSpec(runtime);
|
|
3759
3780
|
server.registerTool(
|
|
3760
3781
|
spec.name,
|
|
3761
3782
|
{
|
|
@@ -3774,9 +3795,7 @@ function registerFlowExamplesTool(server) {
|
|
|
3774
3795
|
|
|
3775
3796
|
// src/tools/flow-load.ts
|
|
3776
3797
|
import { z as z15 } from "zod";
|
|
3777
|
-
import { loadJsonConfig as loadJsonConfig3 } from "@walkeros/cli";
|
|
3778
3798
|
import { mcpResult as mcpResult16, mcpError as mcpError16 } from "@walkeros/core";
|
|
3779
|
-
var API_ID_PREFIX2 = /^(flow|cfg)_/;
|
|
3780
3799
|
var WEB_SKELETON = {
|
|
3781
3800
|
version: 4,
|
|
3782
3801
|
flows: {
|
|
@@ -3798,7 +3817,7 @@ var SERVER_SKELETON = {
|
|
|
3798
3817
|
}
|
|
3799
3818
|
};
|
|
3800
3819
|
var TITLE16 = "Load or Create Flow";
|
|
3801
|
-
var DESCRIPTION14 = "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.";
|
|
3820
|
+
var DESCRIPTION14 = "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.";
|
|
3802
3821
|
var inputSchema14 = {
|
|
3803
3822
|
source: z15.string().optional().describe(
|
|
3804
3823
|
"Flow source: local file path (./flow.json), URL (https://...), inline JSON string, or API flow ID (cfg_...). Omit to create a new flow."
|
|
@@ -3817,19 +3836,19 @@ var annotations16 = {
|
|
|
3817
3836
|
idempotentHint: true,
|
|
3818
3837
|
openWorldHint: true
|
|
3819
3838
|
};
|
|
3820
|
-
function createFlowLoadToolSpec(client) {
|
|
3839
|
+
function createFlowLoadToolSpec(client, runtime) {
|
|
3821
3840
|
return {
|
|
3822
3841
|
name: "flow_load",
|
|
3823
3842
|
title: TITLE16,
|
|
3824
3843
|
description: DESCRIPTION14,
|
|
3825
3844
|
inputSchema: inputSchema14,
|
|
3826
3845
|
annotations: annotations16,
|
|
3827
|
-
handler: (input) => flowLoadHandlerBody(client, input)
|
|
3846
|
+
handler: (input) => flowLoadHandlerBody(client, runtime, input)
|
|
3828
3847
|
};
|
|
3829
3848
|
}
|
|
3830
|
-
async function flowLoadHandlerBody(client, input) {
|
|
3849
|
+
async function flowLoadHandlerBody(client, runtime, input) {
|
|
3831
3850
|
const { source, platform } = input ?? {};
|
|
3832
|
-
if (source &&
|
|
3851
|
+
if (source && isCloudId(source)) {
|
|
3833
3852
|
const resolvedProjectId = resolveDefaultProject(client, void 0);
|
|
3834
3853
|
if (!resolvedProjectId) {
|
|
3835
3854
|
return mcpError16(new Error(NO_DEFAULT_PROJECT_ERROR));
|
|
@@ -3839,9 +3858,8 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
3839
3858
|
flowId: source,
|
|
3840
3859
|
projectId: resolvedProjectId
|
|
3841
3860
|
});
|
|
3842
|
-
const config = flow.config;
|
|
3843
3861
|
return mcpResult16(
|
|
3844
|
-
redactNestedStrings(
|
|
3862
|
+
redactNestedStrings(flowConfigOf(flow), { skip: keepStructural }),
|
|
3845
3863
|
{
|
|
3846
3864
|
next: ["Use flow_validate to check", "Use add-step prompt to modify"]
|
|
3847
3865
|
}
|
|
@@ -3852,7 +3870,7 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
3852
3870
|
}
|
|
3853
3871
|
try {
|
|
3854
3872
|
if (source) {
|
|
3855
|
-
const config = await
|
|
3873
|
+
const config = await runtime.load(source);
|
|
3856
3874
|
return mcpResult16(redactNestedStrings(config, { skip: keepStructural }), {
|
|
3857
3875
|
next: ["Use flow_validate to check", "Use add-step prompt to modify"]
|
|
3858
3876
|
});
|
|
@@ -3872,14 +3890,15 @@ async function flowLoadHandlerBody(client, input) {
|
|
|
3872
3890
|
]
|
|
3873
3891
|
});
|
|
3874
3892
|
} catch (error) {
|
|
3893
|
+
if (error instanceof RuntimeRefusal) return mcpError16(error, error.hint);
|
|
3875
3894
|
const msg = error instanceof Error ? error.message : "";
|
|
3876
3895
|
if (msg.includes("not found") || msg.includes("ENOENT"))
|
|
3877
3896
|
return mcpError16(error, "Check configPath \u2014 expected a flow.json file");
|
|
3878
3897
|
return mcpError16(error);
|
|
3879
3898
|
}
|
|
3880
3899
|
}
|
|
3881
|
-
function registerFlowLoadTool(server, client) {
|
|
3882
|
-
const spec = createFlowLoadToolSpec(client);
|
|
3900
|
+
function registerFlowLoadTool(server, client, runtime) {
|
|
3901
|
+
const spec = createFlowLoadToolSpec(client, runtime);
|
|
3883
3902
|
server.registerTool(
|
|
3884
3903
|
spec.name,
|
|
3885
3904
|
{
|
|
@@ -24517,6 +24536,7 @@ function wrapRegisteredToolsWithTelemetry(server) {
|
|
|
24517
24536
|
}
|
|
24518
24537
|
function createWalkerOSMcpServer(opts) {
|
|
24519
24538
|
const packageVersion = opts.version ?? "0.0.0";
|
|
24539
|
+
const runtime = opts.runtime ?? createHostedRuntime(opts.client);
|
|
24520
24540
|
const server = new McpServer(
|
|
24521
24541
|
{
|
|
24522
24542
|
name: "walkeros-flow",
|
|
@@ -24535,12 +24555,12 @@ function createWalkerOSMcpServer(opts) {
|
|
|
24535
24555
|
registerFrameManageTool(server, opts.client);
|
|
24536
24556
|
registerFeedbackTool(server, opts.client);
|
|
24537
24557
|
registerDiagnosticsTool(server, opts.client, packageVersion);
|
|
24538
|
-
registerFlowValidateTool(server);
|
|
24539
|
-
registerFlowBundleTool(server, opts.client);
|
|
24540
|
-
registerFlowSimulateTool(server, opts.client);
|
|
24541
|
-
registerFlowPushTool(server);
|
|
24542
|
-
registerFlowExamplesTool(server);
|
|
24543
|
-
registerFlowLoadTool(server, opts.client);
|
|
24558
|
+
registerFlowValidateTool(server, runtime);
|
|
24559
|
+
registerFlowBundleTool(server, opts.client, runtime);
|
|
24560
|
+
registerFlowSimulateTool(server, opts.client, runtime);
|
|
24561
|
+
registerFlowPushTool(server, runtime);
|
|
24562
|
+
registerFlowExamplesTool(server, runtime);
|
|
24563
|
+
registerFlowLoadTool(server, opts.client, runtime);
|
|
24544
24564
|
registerPackageSearchTool(server);
|
|
24545
24565
|
registerGetPackageSchemaTool(server);
|
|
24546
24566
|
registerPackageSchemaResources(server);
|
|
@@ -24835,6 +24855,153 @@ function createStreamableHttpHandler(server, opts = {}) {
|
|
|
24835
24855
|
};
|
|
24836
24856
|
}
|
|
24837
24857
|
|
|
24858
|
+
// src/runtime/local.ts
|
|
24859
|
+
import {
|
|
24860
|
+
loadJsonConfig,
|
|
24861
|
+
bundle as bundle2,
|
|
24862
|
+
push,
|
|
24863
|
+
simulateSource,
|
|
24864
|
+
simulateTransformer,
|
|
24865
|
+
simulateCollector,
|
|
24866
|
+
simulateDestination
|
|
24867
|
+
} from "@walkeros/cli";
|
|
24868
|
+
|
|
24869
|
+
// src/runtime/bundle-cache.ts
|
|
24870
|
+
import { createHash } from "crypto";
|
|
24871
|
+
import os from "os";
|
|
24872
|
+
import path from "path";
|
|
24873
|
+
import { mkdir, rm, writeFile } from "fs/promises";
|
|
24874
|
+
import { rmSync } from "fs";
|
|
24875
|
+
import { bundle } from "@walkeros/cli";
|
|
24876
|
+
var MAX_ENTRIES = 8;
|
|
24877
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
24878
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
24879
|
+
var cleanupRegistered = false;
|
|
24880
|
+
function hashConfig(resolvedConfig) {
|
|
24881
|
+
return createHash("sha256").update(resolvedConfig).digest("hex");
|
|
24882
|
+
}
|
|
24883
|
+
function isInlineJsonConfig(resolvedConfig) {
|
|
24884
|
+
const trimmed = resolvedConfig.trimStart();
|
|
24885
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
|
|
24886
|
+
try {
|
|
24887
|
+
JSON.parse(resolvedConfig);
|
|
24888
|
+
return true;
|
|
24889
|
+
} catch {
|
|
24890
|
+
return false;
|
|
24891
|
+
}
|
|
24892
|
+
}
|
|
24893
|
+
function registerProcessCleanup() {
|
|
24894
|
+
if (cleanupRegistered) return;
|
|
24895
|
+
cleanupRegistered = true;
|
|
24896
|
+
const cleanup = () => {
|
|
24897
|
+
for (const entry of cache2.values()) {
|
|
24898
|
+
try {
|
|
24899
|
+
rmSync(entry.dir, { recursive: true, force: true });
|
|
24900
|
+
} catch {
|
|
24901
|
+
}
|
|
24902
|
+
}
|
|
24903
|
+
cache2.clear();
|
|
24904
|
+
};
|
|
24905
|
+
process.once("exit", cleanup);
|
|
24906
|
+
}
|
|
24907
|
+
async function evictIfNeeded() {
|
|
24908
|
+
while (cache2.size > MAX_ENTRIES) {
|
|
24909
|
+
const oldestKey = cache2.keys().next().value;
|
|
24910
|
+
if (oldestKey === void 0) break;
|
|
24911
|
+
const evicted = cache2.get(oldestKey);
|
|
24912
|
+
cache2.delete(oldestKey);
|
|
24913
|
+
if (evicted) await rm(evicted.dir, { recursive: true, force: true });
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24916
|
+
async function getOrBuildBundle(resolvedConfig) {
|
|
24917
|
+
if (!isInlineJsonConfig(resolvedConfig)) return void 0;
|
|
24918
|
+
registerProcessCleanup();
|
|
24919
|
+
const key = hashConfig(resolvedConfig);
|
|
24920
|
+
const cached = cache2.get(key);
|
|
24921
|
+
if (cached) {
|
|
24922
|
+
cache2.delete(key);
|
|
24923
|
+
cache2.set(key, cached);
|
|
24924
|
+
return cached.bundlePath;
|
|
24925
|
+
}
|
|
24926
|
+
const pending = inFlight.get(key);
|
|
24927
|
+
if (pending) return pending;
|
|
24928
|
+
const build = (async () => {
|
|
24929
|
+
const dir = path.join(
|
|
24930
|
+
os.tmpdir(),
|
|
24931
|
+
`walkeros-mcp-bundle-${key.slice(0, 16)}`
|
|
24932
|
+
);
|
|
24933
|
+
await rm(dir, { recursive: true, force: true });
|
|
24934
|
+
await mkdir(dir, { recursive: true });
|
|
24935
|
+
const bundlePath = path.join(dir, "flow.mjs");
|
|
24936
|
+
await writeFile(bundlePath, "", "utf-8");
|
|
24937
|
+
await bundle(resolvedConfig, {
|
|
24938
|
+
target: "simulate",
|
|
24939
|
+
silent: true,
|
|
24940
|
+
buildOverrides: { output: bundlePath, format: "esm", minify: false }
|
|
24941
|
+
});
|
|
24942
|
+
cache2.set(key, { bundlePath, dir });
|
|
24943
|
+
await evictIfNeeded();
|
|
24944
|
+
return bundlePath;
|
|
24945
|
+
})();
|
|
24946
|
+
inFlight.set(key, build);
|
|
24947
|
+
try {
|
|
24948
|
+
return await build;
|
|
24949
|
+
} finally {
|
|
24950
|
+
inFlight.delete(key);
|
|
24951
|
+
}
|
|
24952
|
+
}
|
|
24953
|
+
|
|
24954
|
+
// src/runtime/local.ts
|
|
24955
|
+
function createLocalRuntime() {
|
|
24956
|
+
return {
|
|
24957
|
+
load: (input) => loadJsonConfig(input),
|
|
24958
|
+
bundle: (input, opts) => bundle2(input, {
|
|
24959
|
+
flowName: opts.flowName,
|
|
24960
|
+
stats: opts.stats,
|
|
24961
|
+
buildOverrides: opts.output ? { output: opts.output } : void 0
|
|
24962
|
+
}),
|
|
24963
|
+
async simulate(input, opts) {
|
|
24964
|
+
let bundlePath;
|
|
24965
|
+
try {
|
|
24966
|
+
bundlePath = await getOrBuildBundle(input);
|
|
24967
|
+
} catch {
|
|
24968
|
+
bundlePath = void 0;
|
|
24969
|
+
}
|
|
24970
|
+
const common = { bundlePath, flow: opts.flow, silent: true };
|
|
24971
|
+
switch (opts.stepType) {
|
|
24972
|
+
case "source":
|
|
24973
|
+
return simulateSource(input, opts.event, {
|
|
24974
|
+
sourceId: opts.stepId,
|
|
24975
|
+
...common
|
|
24976
|
+
});
|
|
24977
|
+
case "transformer":
|
|
24978
|
+
return simulateTransformer(
|
|
24979
|
+
input,
|
|
24980
|
+
opts.event,
|
|
24981
|
+
{ transformerId: opts.stepId, ...common, ingest: opts.ingest }
|
|
24982
|
+
);
|
|
24983
|
+
case "collector":
|
|
24984
|
+
return simulateCollector(
|
|
24985
|
+
input,
|
|
24986
|
+
opts.event,
|
|
24987
|
+
{ collectorName: opts.stepId, ...common, state: opts.state }
|
|
24988
|
+
);
|
|
24989
|
+
case "destination":
|
|
24990
|
+
return simulateDestination(
|
|
24991
|
+
input,
|
|
24992
|
+
opts.event,
|
|
24993
|
+
{ destinationId: opts.stepId, ...common }
|
|
24994
|
+
);
|
|
24995
|
+
}
|
|
24996
|
+
},
|
|
24997
|
+
push: (input, event, opts) => push(input, event, {
|
|
24998
|
+
json: true,
|
|
24999
|
+
flow: opts.flow,
|
|
25000
|
+
platform: opts.platform
|
|
25001
|
+
})
|
|
25002
|
+
};
|
|
25003
|
+
}
|
|
25004
|
+
|
|
24838
25005
|
// src/tool-definitions.ts
|
|
24839
25006
|
import { z as z20 } from "zod";
|
|
24840
25007
|
import { schemas as schemas6 } from "@walkeros/cli/dev";
|
|
@@ -24999,7 +25166,7 @@ var TOOL_DEFINITIONS = [
|
|
|
24999
25166
|
{
|
|
25000
25167
|
name: "flow_validate",
|
|
25001
25168
|
title: "Validate Flow",
|
|
25002
|
-
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.",
|
|
25169
|
+
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.",
|
|
25003
25170
|
inputSchema: schemas6.ValidateInputShape,
|
|
25004
25171
|
annotations: {
|
|
25005
25172
|
readOnlyHint: true,
|
|
@@ -25037,10 +25204,11 @@ var TOOL_DEFINITIONS = [
|
|
|
25037
25204
|
verbose: z20.boolean().optional()
|
|
25038
25205
|
},
|
|
25039
25206
|
annotations: {
|
|
25040
|
-
|
|
25041
|
-
|
|
25042
|
-
|
|
25043
|
-
|
|
25207
|
+
// Not read-only: simulation compiles and runs the flow in process.
|
|
25208
|
+
readOnlyHint: false,
|
|
25209
|
+
destructiveHint: true,
|
|
25210
|
+
idempotentHint: false,
|
|
25211
|
+
openWorldHint: true
|
|
25044
25212
|
}
|
|
25045
25213
|
},
|
|
25046
25214
|
{
|
|
@@ -25063,7 +25231,7 @@ var TOOL_DEFINITIONS = [
|
|
|
25063
25231
|
{
|
|
25064
25232
|
name: "flow_examples",
|
|
25065
25233
|
title: "Flow Examples",
|
|
25066
|
-
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.",
|
|
25234
|
+
description: "List all step examples in a walkerOS flow configuration. Shows example names, step locations, and in/out shapes. 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.",
|
|
25067
25235
|
inputSchema: {
|
|
25068
25236
|
configPath: z20.string().min(1),
|
|
25069
25237
|
flow: z20.string().optional(),
|
|
@@ -25081,7 +25249,7 @@ var TOOL_DEFINITIONS = [
|
|
|
25081
25249
|
{
|
|
25082
25250
|
name: "flow_load",
|
|
25083
25251
|
title: "Load or Create Flow",
|
|
25084
|
-
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.",
|
|
25252
|
+
description: "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.",
|
|
25085
25253
|
inputSchema: {
|
|
25086
25254
|
source: z20.string().optional(),
|
|
25087
25255
|
platform: z20.enum(["web", "server"]).optional()
|
|
@@ -25172,7 +25340,7 @@ var TOOL_DEFINITIONS = [
|
|
|
25172
25340
|
];
|
|
25173
25341
|
|
|
25174
25342
|
// src/index.ts
|
|
25175
|
-
function createToolHandlers(client, packageVersion = "0.0.0") {
|
|
25343
|
+
function createToolHandlers(client, packageVersion = "0.0.0", runtime = createHostedRuntime(client)) {
|
|
25176
25344
|
const specs = [
|
|
25177
25345
|
createAuthToolSpec(client),
|
|
25178
25346
|
createProjectManageToolSpec(client),
|
|
@@ -25184,12 +25352,12 @@ function createToolHandlers(client, packageVersion = "0.0.0") {
|
|
|
25184
25352
|
createHubManageToolSpec(client),
|
|
25185
25353
|
createFrameManageToolSpec(client),
|
|
25186
25354
|
createFeedbackToolSpec(client),
|
|
25187
|
-
createFlowValidateToolSpec(),
|
|
25188
|
-
createFlowBundleToolSpec(client),
|
|
25189
|
-
createFlowSimulateToolSpec(client),
|
|
25190
|
-
createFlowPushToolSpec(),
|
|
25191
|
-
createFlowExamplesToolSpec(),
|
|
25192
|
-
createFlowLoadToolSpec(client),
|
|
25355
|
+
createFlowValidateToolSpec(runtime),
|
|
25356
|
+
createFlowBundleToolSpec(client, runtime),
|
|
25357
|
+
createFlowSimulateToolSpec(client, runtime),
|
|
25358
|
+
createFlowPushToolSpec(runtime),
|
|
25359
|
+
createFlowExamplesToolSpec(runtime),
|
|
25360
|
+
createFlowLoadToolSpec(client, runtime),
|
|
25193
25361
|
createPackageSearchToolSpec(),
|
|
25194
25362
|
createPackageGetToolSpec(),
|
|
25195
25363
|
createDiagnosticsToolSpec(client, packageVersion)
|
|
@@ -25249,7 +25417,10 @@ export {
|
|
|
25249
25417
|
HUB_NOT_FOUND_HINT,
|
|
25250
25418
|
HttpToolClient,
|
|
25251
25419
|
DESCRIPTION7 as OBSERVE_SESSION_DESCRIPTION,
|
|
25420
|
+
RuntimeRefusal,
|
|
25252
25421
|
TOOL_DEFINITIONS,
|
|
25422
|
+
createHostedRuntime,
|
|
25423
|
+
createLocalRuntime,
|
|
25253
25424
|
createStreamableHttpHandler,
|
|
25254
25425
|
createToolHandlers,
|
|
25255
25426
|
createWalkerOSMcpServer,
|