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