@walkeros/mcp 3.4.1-next-1776790594143 → 3.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -156,7 +156,7 @@ function registerFlowBundleTool(server2) {
156
156
  inputSchema: {
157
157
  ...schemas2.BundleInputShape,
158
158
  remote: z2.boolean().optional().describe(
159
- "Use remote cloud bundling (requires WALKEROS_TOKEN). Default: false (local)"
159
+ "Use remote cloud bundling (requires authentication). Default: false (local)"
160
160
  ),
161
161
  content: z2.record(z2.string(), z2.unknown()).optional().describe("Flow.Config JSON content (required when remote: true)")
162
162
  },
@@ -182,7 +182,7 @@ function registerFlowBundleTool(server2) {
182
182
  {
183
183
  next: [
184
184
  "Use flow_simulate to test",
185
- "Use api({ action: 'deploy' }) to publish"
185
+ 'Use deploy_manage with action "deploy" to publish'
186
186
  ]
187
187
  }
188
188
  );
@@ -209,7 +209,7 @@ function registerFlowBundleTool(server2) {
209
209
  {
210
210
  next: [
211
211
  "Use flow_simulate to test",
212
- "Use api({ action: 'deploy' }) to publish"
212
+ 'Use deploy_manage with action "deploy" to publish'
213
213
  ]
214
214
  }
215
215
  );
@@ -863,7 +863,11 @@ function registerFlowLoadTool(server2) {
863
863
 
864
864
  // src/tools/feedback.ts
865
865
  import { z as z8 } from "zod";
866
- import { feedback, readConfig, writeConfig } from "@walkeros/cli";
866
+ import {
867
+ feedback,
868
+ getFeedbackPreference,
869
+ setFeedbackPreference
870
+ } from "@walkeros/cli";
867
871
  import { mcpResult as mcpResult8, mcpError as mcpError8 } from "@walkeros/core";
868
872
  function registerFeedbackTool(server2) {
869
873
  server2.registerTool(
@@ -887,8 +891,7 @@ function registerFeedbackTool(server2) {
887
891
  async (params) => {
888
892
  try {
889
893
  const { text, anonymous: explicitAnonymous } = params;
890
- const config = readConfig();
891
- let anonymous = config?.anonymousFeedback;
894
+ let anonymous = getFeedbackPreference();
892
895
  if (anonymous === void 0 && explicitAnonymous === void 0) {
893
896
  return mcpResult8(
894
897
  { needsConsent: true },
@@ -902,11 +905,10 @@ function registerFeedbackTool(server2) {
902
905
  }
903
906
  if (anonymous === void 0 && explicitAnonymous !== void 0) {
904
907
  anonymous = explicitAnonymous;
905
- const base = config ?? { token: "", email: "", appUrl: "" };
906
- writeConfig({ ...base, anonymousFeedback: anonymous });
908
+ setFeedbackPreference(anonymous);
907
909
  }
908
910
  const isAnonymous = explicitAnonymous ?? anonymous ?? true;
909
- await feedback(text, { anonymous: isAnonymous, version: "3.4.1-next-1776790594143" });
911
+ await feedback(text, { anonymous: isAnonymous, version: "3.4.2" });
910
912
  return mcpResult8({ ok: true });
911
913
  } catch (error) {
912
914
  return mcpError8(error);
@@ -915,102 +917,332 @@ function registerFeedbackTool(server2) {
915
917
  );
916
918
  }
917
919
 
918
- // src/tools/api.ts
920
+ // src/tools/auth.ts
921
+ import { z as z9 } from "zod";
922
+ import {
923
+ resolveToken,
924
+ deleteConfig,
925
+ requestDeviceCode,
926
+ pollForToken,
927
+ whoami
928
+ } from "@walkeros/cli";
929
+ import { mcpResult as mcpResult9, mcpError as mcpError9 } from "@walkeros/core";
930
+ function registerAuthTool(server2) {
931
+ server2.registerTool(
932
+ "auth",
933
+ {
934
+ title: "Authentication",
935
+ description: "Manage walkerOS authentication. Check login status, log in via device code flow, or log out. No terminal or browser required \u2014 the MCP client handles the authorization URL.",
936
+ inputSchema: {
937
+ action: z9.enum(["status", "login", "logout"]).describe("Authentication action to perform"),
938
+ deviceCode: z9.string().optional().describe(
939
+ "Device code from a previous pending login attempt. Provide to resume polling without requesting a new code."
940
+ )
941
+ },
942
+ // No outputSchema: action-dispatched tool — each action returns a different shape
943
+ annotations: {
944
+ readOnlyHint: false,
945
+ destructiveHint: true,
946
+ idempotentHint: false,
947
+ openWorldHint: true
948
+ }
949
+ },
950
+ async ({ action, deviceCode }) => {
951
+ try {
952
+ switch (action) {
953
+ case "status": {
954
+ const resolved = resolveToken();
955
+ if (!resolved) {
956
+ return mcpResult9(
957
+ { authenticated: false },
958
+ { next: ['Use auth with action "login" to authenticate'] }
959
+ );
960
+ }
961
+ const user = await whoami();
962
+ return mcpResult9({ authenticated: true, ...user });
963
+ }
964
+ case "login": {
965
+ if (deviceCode) {
966
+ const pollResult = await pollForToken(deviceCode, {
967
+ timeoutMs: 6e4
968
+ });
969
+ if (pollResult.success) {
970
+ return mcpResult9(
971
+ { authenticated: true, email: pollResult.email },
972
+ {
973
+ next: [
974
+ 'Use project_manage with action "list" to see your projects'
975
+ ]
976
+ }
977
+ );
978
+ }
979
+ if (pollResult.status === "pending") {
980
+ return mcpResult9({
981
+ authenticated: false,
982
+ status: "pending",
983
+ message: "Still waiting for authorization. Try again shortly.",
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
+ });
1000
+ }
1001
+ case "logout": {
1002
+ const deleted = deleteConfig();
1003
+ const hadEnvToken = typeof process.env.WALKEROS_TOKEN === "string" && process.env.WALKEROS_TOKEN.length > 0;
1004
+ delete process.env.WALKEROS_TOKEN;
1005
+ let message;
1006
+ if (deleted && hadEnvToken) {
1007
+ message = "Logged out. Config removed and WALKEROS_TOKEN cleared from process environment.";
1008
+ } else if (deleted) {
1009
+ message = "Logged out and config removed.";
1010
+ } else if (hadEnvToken) {
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
+ });
1019
+ }
1020
+ default:
1021
+ throw new Error(
1022
+ `Unknown action: ${action}. Use one of: status, login, logout`
1023
+ );
1024
+ }
1025
+ } catch (error) {
1026
+ return mcpError9(error);
1027
+ }
1028
+ }
1029
+ );
1030
+ }
1031
+
1032
+ // src/tools/project-manage.ts
919
1033
  import { z as z10 } from "zod";
920
1034
  import {
921
- whoami,
922
1035
  listProjects,
923
1036
  getProject,
924
1037
  createProject,
925
1038
  updateProject,
926
1039
  deleteProject,
1040
+ setDefaultProject
1041
+ } from "@walkeros/cli";
1042
+ import { mcpResult as mcpResult10, mcpError as mcpError10 } from "@walkeros/core";
1043
+
1044
+ // src/types.ts
1045
+ function isAuthError(error) {
1046
+ if (!(error instanceof Error)) return false;
1047
+ const msg = error.message.toLowerCase();
1048
+ if (msg.includes("unauthorized") || msg.includes("forbidden") || msg.includes("invalid token") || msg.includes("token expired") || msg.includes("not authenticated")) {
1049
+ return true;
1050
+ }
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
+ }
1056
+ var AUTH_HINT = 'Are you logged in? Use auth(action: "status") to check.';
1057
+
1058
+ // src/tools/project-manage.ts
1059
+ function registerProjectManageTool(server2) {
1060
+ server2.registerTool(
1061
+ "project_manage",
1062
+ {
1063
+ title: "Project Management",
1064
+ description: "Manage walkerOS projects. List, create, update, delete projects, or set a default project for CLI operations.",
1065
+ inputSchema: {
1066
+ action: z10.enum(["list", "get", "create", "update", "delete", "set_default"]).describe("Project management action to perform"),
1067
+ projectId: z10.string().optional().describe(
1068
+ "Project ID. Required for get, update, delete, and set_default actions."
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
+ }
1081
+ },
1082
+ async ({ action, projectId, name }) => {
1083
+ try {
1084
+ switch (action) {
1085
+ case "list": {
1086
+ const projects = await listProjects();
1087
+ const items = Array.isArray(projects) ? projects : projects?.projects || [];
1088
+ if (items.length === 0) {
1089
+ return mcpResult10(
1090
+ { projects: [] },
1091
+ {
1092
+ next: [
1093
+ 'Use project_manage with action "create" to create your first project',
1094
+ 'Use project_manage with action "set_default" after creating to set it as default'
1095
+ ]
1096
+ }
1097
+ );
1098
+ }
1099
+ return mcpResult10(projects);
1100
+ }
1101
+ case "get": {
1102
+ if (!projectId) {
1103
+ return mcpError10(
1104
+ new Error(
1105
+ 'projectId is required for get action. Use action "list" to see available projects.'
1106
+ )
1107
+ );
1108
+ }
1109
+ const project = await getProject({ projectId });
1110
+ return mcpResult10(project);
1111
+ }
1112
+ case "create": {
1113
+ if (!name) {
1114
+ return mcpError10(new Error("name is required for create action."));
1115
+ }
1116
+ const created = await createProject({ name });
1117
+ return mcpResult10(created, {
1118
+ next: [
1119
+ 'Use project_manage with action "set_default" to make this your active project'
1120
+ ]
1121
+ });
1122
+ }
1123
+ case "update": {
1124
+ if (!projectId) {
1125
+ return mcpError10(
1126
+ new Error(
1127
+ 'projectId is required for update action. Use action "list" to see available projects.'
1128
+ )
1129
+ );
1130
+ }
1131
+ if (!name) {
1132
+ return mcpError10(new Error("name is required for update action."));
1133
+ }
1134
+ const updated = await updateProject({ projectId, name });
1135
+ return mcpResult10(updated);
1136
+ }
1137
+ case "delete": {
1138
+ if (!projectId) {
1139
+ return mcpError10(
1140
+ new Error(
1141
+ 'projectId is required for delete action. Use action "list" to see available projects.'
1142
+ )
1143
+ );
1144
+ }
1145
+ const deleted = await deleteProject({ projectId });
1146
+ return mcpResult10(deleted);
1147
+ }
1148
+ case "set_default": {
1149
+ if (!projectId) {
1150
+ return mcpError10(
1151
+ new Error(
1152
+ 'projectId is required for set_default action. Use action "list" to see available projects.'
1153
+ )
1154
+ );
1155
+ }
1156
+ setDefaultProject(projectId);
1157
+ return mcpResult10(
1158
+ { defaultProjectId: projectId },
1159
+ {
1160
+ next: [
1161
+ 'Use flow_manage with action "list" to see flows in this project'
1162
+ ]
1163
+ }
1164
+ );
1165
+ }
1166
+ default:
1167
+ throw new Error(
1168
+ `Unknown action: ${action}. Use one of: list, get, create, update, delete, set_default`
1169
+ );
1170
+ }
1171
+ } catch (error) {
1172
+ return mcpError10(error, isAuthError(error) ? AUTH_HINT : void 0);
1173
+ }
1174
+ }
1175
+ );
1176
+ }
1177
+
1178
+ // src/tools/flow-manage.ts
1179
+ import { z as z11 } from "zod";
1180
+ import {
1181
+ listAllFlows,
927
1182
  listFlows,
928
1183
  getFlow,
929
1184
  createFlow,
930
1185
  updateFlow,
931
1186
  deleteFlow,
932
1187
  duplicateFlow,
933
- deploy,
934
- getDeployment,
935
- listDeployments,
936
- getDeploymentBySlug,
937
- createDeployment as createDep,
938
- deleteDeployment as deleteDep,
939
- listPreviews as listPreviewsApi,
940
- getPreview as getPreviewApi,
941
- createPreview as createPreviewApi,
942
- deletePreview as deletePreviewApi
1188
+ listPreviews,
1189
+ getPreview,
1190
+ createPreview,
1191
+ deletePreview
943
1192
  } from "@walkeros/cli";
944
- import { mcpResult as mcpResult9, mcpError as mcpError9 } from "@walkeros/core";
945
-
946
- // src/schemas/api-output.ts
947
- import { z as z9 } from "zod";
948
- var ApiOutputShape = {
949
- action: z9.string().describe("Action that was executed"),
950
- ok: z9.boolean().describe("Whether the action succeeded"),
951
- data: z9.unknown().describe("Action-specific result data")
952
- };
953
-
954
- // src/tools/api.ts
955
- var ACTIONS = [
956
- "whoami",
957
- "project.list",
958
- "project.get",
959
- "project.create",
960
- "project.update",
961
- "project.delete",
962
- "flow.list",
963
- "flow.get",
964
- "flow.create",
965
- "flow.update",
966
- "flow.delete",
967
- "flow.duplicate",
968
- "deploy",
969
- "deployment.get",
970
- "deployment.list",
971
- "deployment.create",
972
- "deployment.delete",
973
- "preview.list",
974
- "preview.get",
975
- "preview.create",
976
- "preview.delete"
977
- ];
978
- function registerApiTool(server2) {
1193
+ import { mcpResult as mcpResult11, mcpError as mcpError11 } from "@walkeros/core";
1194
+ function registerFlowManageTool(server2) {
979
1195
  server2.registerTool(
980
- "api",
1196
+ "flow_manage",
981
1197
  {
982
- title: "walkerOS Cloud API",
983
- description: "Manage walkerOS cloud projects, flows, and deployments. Requires WALKEROS_TOKEN env var.\n\nActions:\n- whoami \u2014 verify token, get user info\n- project.list/get/create/update/delete \u2014 manage projects\n- flow.list/get/create/update/delete/duplicate \u2014 manage flow configs\n- deploy \u2014 deploy a flow (auto-detects web/server)\n- deployment.get/list/create/delete \u2014 manage deployments\n- preview.list/get/create/delete \u2014 manage preview bundles for testing flow changes on live sites\n\nParameters vary by action. content = Flow.Config JSON for flow.create/update.\n\nFor preview.create:\n- Ask the user for their site URL BEFORE creating (pass as siteUrl).\n- After creation, tell the user to open the returned activationUrl to activate preview mode.\n- Tell them to open the returned deactivationUrl to exit preview mode.\n- The activation URL is first-party on THEIR site (not walkeros.io); the preview only affects their domain.\n- Show the URLs verbatim; do not paraphrase them.\n",
1198
+ title: "Flow Management",
1199
+ 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.",
984
1200
  inputSchema: {
985
- action: z10.enum(ACTIONS).describe("API action to perform"),
986
- projectId: z10.string().optional().describe(
987
- "Project ID (proj_...). Required for: project.get/update/delete, flow.create, flow.list. Falls back to WALKEROS_PROJECT_ID env var."
1201
+ action: z11.enum([
1202
+ "list",
1203
+ "get",
1204
+ "create",
1205
+ "update",
1206
+ "delete",
1207
+ "duplicate",
1208
+ "preview_list",
1209
+ "preview_get",
1210
+ "preview_create",
1211
+ "preview_delete"
1212
+ ]).describe("Flow management action to perform"),
1213
+ flowId: z11.string().optional().describe(
1214
+ "Flow ID (flow_...) or config ID (cfg_...). Required for get, update, delete, duplicate."
1215
+ ),
1216
+ projectId: z11.string().optional().describe(
1217
+ "Project ID. Optional filter for list (omit to list all projects). Required for create if no default project set."
988
1218
  ),
989
- flowId: z10.string().optional().describe(
990
- "Flow ID (flow_...) or config ID (cfg_...). Required for: flow.get, flow.update, flow.delete, flow.duplicate, deploy. For deployment.get/delete: can be a deployment slug."
1219
+ name: z11.string().optional().describe(
1220
+ "Flow name. Required for create. Optional for update (to rename) and duplicate."
991
1221
  ),
992
- name: z10.string().optional().describe("Name for create/update operations"),
993
- content: z10.record(z10.string(), z10.unknown()).optional().describe("Flow.Config JSON for flow operations"),
994
- patch: z10.boolean().optional().describe("Use merge-patch for flow.update (default: true)"),
995
- wait: z10.boolean().optional().describe("Wait for deploy to complete (default: true)"),
996
- flowName: z10.string().optional().describe("Flow name for multi-settings flows"),
997
- fields: z10.array(z10.string()).optional().describe("Dot-path field selectors for flow.get"),
998
- type: z10.enum(["web", "server"]).optional().describe("Deployment type for deployment.create"),
999
- sort: z10.string().optional().describe("Sort field for list operations"),
1000
- order: z10.enum(["asc", "desc"]).optional().describe("Sort order"),
1001
- status: z10.string().optional().describe("Status filter for deployment.list"),
1002
- includeDeleted: z10.boolean().optional().describe("Include deleted items in lists"),
1003
- previewId: z10.string().optional().describe(
1004
- "Preview ID (e.g., prv_abc123). Required for preview.get/delete."
1222
+ content: z11.record(z11.string(), z11.unknown()).optional().describe("Flow.Config JSON content. Used for create and update."),
1223
+ patch: z11.boolean().optional().describe(
1224
+ "Merge-patch for update (default true). When true, only provided fields are updated."
1005
1225
  ),
1006
- flowSettingsId: z10.string().optional().describe(
1007
- "Flow settings ID. Used by preview.create as an alternative to flowName."
1226
+ fields: z11.array(z11.string()).optional().describe(
1227
+ "Dot-path selectors for get to return only specific fields."
1008
1228
  ),
1009
- siteUrl: z10.string().optional().describe(
1010
- "Optional site URL for preview.create. When provided, the response includes a full activationUrl the user can click to activate preview mode on their site."
1229
+ sort: z11.enum(["name", "updated_at", "created_at"]).optional().describe("Sort field for list."),
1230
+ order: z11.enum(["asc", "desc"]).optional().describe("Sort order for list."),
1231
+ includeDeleted: z11.boolean().optional().describe("Include soft-deleted flows in list results."),
1232
+ previewId: z11.string().optional().describe(
1233
+ "Preview ID (prv_...). Required for preview_get and preview_delete."
1234
+ ),
1235
+ flowName: z11.string().optional().describe(
1236
+ "Flow settings name. Used by preview_create as an alternative to flowSettingsId."
1237
+ ),
1238
+ flowSettingsId: z11.string().optional().describe(
1239
+ "Flow settings ID. Used by preview_create as an alternative to flowName."
1240
+ ),
1241
+ siteUrl: z11.string().optional().describe(
1242
+ "Optional site URL for preview_create. When provided, the response includes full activationUrl and deactivationUrl the user can click."
1011
1243
  )
1012
1244
  },
1013
- outputSchema: ApiOutputShape,
1245
+ // No outputSchema: action-dispatched tool — each action returns a different shape
1014
1246
  annotations: {
1015
1247
  readOnlyHint: false,
1016
1248
  destructiveHint: true,
@@ -1018,249 +1250,152 @@ function registerApiTool(server2) {
1018
1250
  openWorldHint: true
1019
1251
  }
1020
1252
  },
1021
- async (params, extra) => {
1022
- const {
1023
- action,
1024
- projectId,
1025
- flowId,
1026
- name,
1027
- content,
1028
- patch,
1029
- wait,
1030
- flowName,
1031
- fields,
1032
- type,
1033
- sort,
1034
- order,
1035
- status,
1036
- includeDeleted,
1037
- previewId,
1038
- flowSettingsId,
1039
- siteUrl
1040
- } = params;
1253
+ async ({
1254
+ action,
1255
+ flowId,
1256
+ projectId,
1257
+ name,
1258
+ content,
1259
+ patch,
1260
+ fields,
1261
+ sort,
1262
+ order,
1263
+ includeDeleted,
1264
+ previewId,
1265
+ flowName,
1266
+ flowSettingsId,
1267
+ siteUrl
1268
+ }) => {
1041
1269
  try {
1042
- let data;
1043
1270
  switch (action) {
1044
- // Auth
1045
- case "whoami": {
1046
- data = await whoami();
1047
- break;
1048
- }
1049
- // Projects
1050
- case "project.list": {
1051
- data = await listProjects();
1052
- break;
1053
- }
1054
- case "project.get": {
1055
- data = await getProject({ projectId });
1056
- break;
1057
- }
1058
- case "project.create": {
1059
- if (!name) throw new Error("name required for project.create");
1060
- data = await createProject({ name });
1061
- break;
1062
- }
1063
- case "project.update": {
1064
- if (!name) throw new Error("name required for project.update");
1065
- data = await updateProject({ projectId, name });
1066
- break;
1067
- }
1068
- case "project.delete": {
1069
- data = await deleteProject({ projectId });
1070
- break;
1271
+ case "list": {
1272
+ if (projectId) {
1273
+ const data2 = await listFlows({
1274
+ projectId,
1275
+ sort,
1276
+ order,
1277
+ includeDeleted
1278
+ });
1279
+ return mcpResult11(data2);
1280
+ }
1281
+ const data = await listAllFlows({ sort, order, includeDeleted });
1282
+ return mcpResult11(
1283
+ { projects: data },
1284
+ {
1285
+ next: [
1286
+ 'Use flow_manage with action "get" and a flowId to inspect a specific flow',
1287
+ "Use flow_load to open a flow for editing"
1288
+ ]
1289
+ }
1290
+ );
1071
1291
  }
1072
- // Flows
1073
- case "flow.list": {
1074
- data = await listFlows({
1075
- projectId,
1076
- sort,
1077
- order,
1078
- includeDeleted
1292
+ case "get": {
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
+ ]
1079
1305
  });
1080
- break;
1081
1306
  }
1082
- case "flow.get": {
1083
- if (!flowId) throw new Error("flowId required for flow.get");
1084
- data = await getFlow({ flowId, projectId, fields });
1085
- break;
1086
- }
1087
- case "flow.create": {
1088
- if (!name) throw new Error("name required for flow.create");
1089
- if (!content) throw new Error("content required for flow.create");
1090
- data = await createFlow({ name, content, projectId });
1091
- break;
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
+ });
1092
1321
  }
1093
- case "flow.update": {
1094
- if (!flowId) throw new Error("flowId required for flow.update");
1095
- data = await updateFlow({
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({
1096
1331
  flowId,
1332
+ projectId,
1097
1333
  name,
1098
1334
  content,
1099
- projectId,
1100
1335
  mergePatch: patch ?? true
1101
1336
  });
1102
- break;
1337
+ return mcpResult11(updated);
1103
1338
  }
1104
- case "flow.delete": {
1105
- if (!flowId) throw new Error("flowId required for flow.delete");
1106
- data = await deleteFlow({ flowId, projectId });
1107
- break;
1108
- }
1109
- case "flow.duplicate": {
1110
- if (!flowId) throw new Error("flowId required for flow.duplicate");
1111
- data = await duplicateFlow({ flowId, name, projectId });
1112
- break;
1113
- }
1114
- // Deploy
1115
- case "deploy": {
1116
- if (!flowId) throw new Error("flowId required for deploy");
1117
- const progressToken = extra._meta?.progressToken;
1118
- const DEPLOY_TIMEOUT_MS = 9e4;
1119
- const timeoutSignal = AbortSignal.timeout(DEPLOY_TIMEOUT_MS);
1120
- const combinedAbort = new AbortController();
1121
- const onAbort = () => combinedAbort.abort();
1122
- timeoutSignal.addEventListener("abort", onAbort);
1123
- extra.signal?.addEventListener("abort", onAbort);
1124
- data = await deploy({
1125
- flowId,
1126
- projectId,
1127
- wait: wait ?? true,
1128
- flowName,
1129
- timeout: DEPLOY_TIMEOUT_MS,
1130
- onStatus: (s, sub) => {
1131
- if (!progressToken) return;
1132
- const key = sub ? `${s}:${sub}` : s;
1133
- const stages = {
1134
- "bundling:building": {
1135
- progress: 20,
1136
- label: "Building bundle..."
1137
- },
1138
- "deploying:publishing": {
1139
- progress: 60,
1140
- label: "Publishing to CDN..."
1141
- },
1142
- "deploying:provisioning": {
1143
- progress: 60,
1144
- label: "Provisioning container..."
1145
- },
1146
- "deploying:starting": {
1147
- progress: 80,
1148
- label: "Starting container..."
1149
- },
1150
- published: { progress: 100, label: "Published" },
1151
- active: { progress: 100, label: "Active" },
1152
- failed: { progress: 100, label: "Failed" }
1153
- };
1154
- const stage = stages[key] ?? stages[s] ?? { progress: 10, label: key };
1155
- extra.sendNotification({
1156
- method: "notifications/progress",
1157
- params: {
1158
- progressToken,
1159
- progress: stage.progress,
1160
- total: 100,
1161
- message: stage.label
1162
- }
1163
- });
1164
- },
1165
- signal: combinedAbort.signal
1166
- });
1167
- timeoutSignal.removeEventListener("abort", onAbort);
1168
- extra.signal?.removeEventListener("abort", onAbort);
1169
- const st = data.status;
1170
- const deployData = data;
1171
- if (st === "failed") {
1172
- return mcpResult9(
1173
- { action, ok: false, data },
1174
- {
1175
- next: ["Run flow_validate to check your configuration"]
1176
- }
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
+ )
1177
1345
  );
1178
- } else {
1179
- const publicUrl = deployData.publicUrl;
1180
- const containerUrl = deployData.containerUrl;
1181
- const deployType = deployData.type;
1182
- const nextHints = [];
1183
- if (deployType === "web" && publicUrl) {
1184
- nextHints.push(`Bundle at ${publicUrl}`);
1185
- nextHints.push(`Add <script src='${publicUrl}'></script>`);
1186
- } else if (deployType === "server" && containerUrl) {
1187
- nextHints.push(`Container at ${containerUrl}`);
1188
- nextHints.push(`Test: curl ${containerUrl}/health`);
1189
- }
1190
- if (nextHints.length > 0) {
1191
- return mcpResult9(
1192
- { action, ok: true, data },
1193
- {
1194
- next: nextHints
1195
- }
1196
- );
1197
- }
1198
1346
  }
1199
- break;
1347
+ const deleted = await deleteFlow({ flowId, projectId });
1348
+ return mcpResult11(deleted);
1200
1349
  }
1201
- // Deployments
1202
- case "deployment.get": {
1203
- if (!flowId)
1204
- throw new Error(
1205
- "flowId (flowId or slug) required for deployment.get"
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
+ )
1206
1356
  );
1207
- try {
1208
- data = await getDeployment({ flowId, flowName });
1209
- } catch {
1210
- data = await getDeploymentBySlug({ slug: flowId });
1211
1357
  }
1212
- break;
1213
- }
1214
- case "deployment.list": {
1215
- data = await listDeployments({
1216
- projectId,
1217
- type,
1218
- status
1358
+ const duplicated = await duplicateFlow({
1359
+ flowId,
1360
+ name,
1361
+ projectId
1219
1362
  });
1220
- break;
1363
+ return mcpResult11(duplicated);
1221
1364
  }
1222
- case "deployment.create": {
1223
- if (!type)
1224
- throw new Error(
1225
- "type (web/server) required for deployment.create"
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
+ )
1226
1371
  );
1227
- data = await createDep({ type, label: name, projectId });
1228
- break;
1229
- }
1230
- case "deployment.delete": {
1231
- if (!flowId)
1232
- throw new Error("flowId (slug) required for deployment.delete");
1233
- data = await deleteDep({ slug: flowId });
1234
- break;
1235
- }
1236
- // Previews
1237
- case "preview.list": {
1238
- if (!flowId) throw new Error("flowId required for preview.list");
1239
- data = await listPreviewsApi({ projectId, flowId });
1240
- break;
1372
+ }
1373
+ const data = await listPreviews({ projectId, flowId });
1374
+ return mcpResult11(data);
1241
1375
  }
1242
- case "preview.get": {
1376
+ case "preview_get": {
1243
1377
  if (!flowId || !previewId) {
1244
- throw new Error("flowId and previewId required for preview.get");
1378
+ return mcpError11(
1379
+ new Error("flowId and previewId are required for preview_get.")
1380
+ );
1245
1381
  }
1246
- data = await getPreviewApi({ projectId, flowId, previewId });
1247
- break;
1382
+ const data = await getPreview({ projectId, flowId, previewId });
1383
+ return mcpResult11(data);
1248
1384
  }
1249
- case "preview.create": {
1250
- if (!flowId) throw new Error("flowId required for preview.create");
1251
- if (!flowName && !flowSettingsId) {
1252
- throw new Error(
1253
- "flowName or flowSettingsId required for preview.create"
1385
+ case "preview_create": {
1386
+ if (!flowId) {
1387
+ return mcpError11(
1388
+ new Error("flowId is required for preview_create.")
1254
1389
  );
1255
1390
  }
1256
- if (siteUrl) {
1257
- try {
1258
- new URL(siteUrl);
1259
- } catch {
1260
- throw new Error(`Invalid siteUrl: ${siteUrl}`);
1261
- }
1391
+ if (!flowName && !flowSettingsId) {
1392
+ return mcpError11(
1393
+ new Error(
1394
+ "flowName or flowSettingsId is required for preview_create."
1395
+ )
1396
+ );
1262
1397
  }
1263
- const preview = await createPreviewApi({
1398
+ const preview = await createPreview({
1264
1399
  projectId,
1265
1400
  flowId,
1266
1401
  flowName,
@@ -1281,52 +1416,155 @@ function registerApiTool(server2) {
1281
1416
  } else {
1282
1417
  delete enriched.activationUrl;
1283
1418
  }
1284
- data = enriched;
1285
- break;
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
+ });
1286
1426
  }
1287
- case "preview.delete": {
1427
+ case "preview_delete": {
1288
1428
  if (!flowId || !previewId) {
1289
- throw new Error(
1290
- "flowId and previewId required for preview.delete"
1429
+ return mcpError11(
1430
+ new Error(
1431
+ "flowId and previewId are required for preview_delete."
1432
+ )
1291
1433
  );
1292
1434
  }
1293
- data = await deletePreviewApi({ projectId, flowId, previewId });
1294
- break;
1435
+ const data = await deletePreview({
1436
+ projectId,
1437
+ flowId,
1438
+ previewId
1439
+ });
1440
+ return mcpResult11(data);
1295
1441
  }
1296
1442
  default:
1297
1443
  throw new Error(
1298
- `Unknown action: ${action}. Use one of: ${ACTIONS.join(", ")}`
1444
+ `Unknown action: ${action}. Use one of: list, get, create, update, delete, duplicate, preview_list, preview_get, preview_create, preview_delete`
1299
1445
  );
1300
1446
  }
1301
- return mcpResult9({ action, ok: true, data });
1302
1447
  } catch (error) {
1303
- const msg = error instanceof Error ? error.message : "";
1304
- const name2 = error instanceof Error ? error.name : "";
1305
- if (action === "deploy" && (name2 === "AbortError" || name2 === "TimeoutError" || msg.includes("abort"))) {
1306
- return mcpResult9(
1307
- {
1308
- action,
1309
- ok: true,
1310
- data: { status: "deploying", flowId }
1311
- },
1312
- {
1448
+ return mcpError11(error, isAuthError(error) ? AUTH_HINT : void 0);
1449
+ }
1450
+ }
1451
+ );
1452
+ }
1453
+
1454
+ // src/tools/deploy-manage.ts
1455
+ import { z as z12 } from "zod";
1456
+ import {
1457
+ deploy as deployFlow,
1458
+ listDeployments,
1459
+ getDeployment,
1460
+ getDeploymentBySlug,
1461
+ deleteDeployment
1462
+ } from "@walkeros/cli";
1463
+ import { mcpResult as mcpResult12, mcpError as mcpError12 } from "@walkeros/core";
1464
+ function registerDeployTool(server2) {
1465
+ server2.registerTool(
1466
+ "deploy_manage",
1467
+ {
1468
+ title: "Deploy Management",
1469
+ description: 'Deploy walkerOS flows and manage deployments. Deploy a flow, list deployments, get deployment details, or delete deployments. Note: the "get" and "delete" actions accept a deployment slug (e.g. "dep_..."), not a flow ID. If you only have a flowId, resolve the latest deployment slug first via flow_manage with action "get".',
1470
+ inputSchema: {
1471
+ action: z12.enum(["deploy", "list", "get", "delete"]).describe("Deployment action to perform"),
1472
+ flowId: z12.string().optional().describe("Flow ID. Required for deploy action."),
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
+ }
1493
+ },
1494
+ async ({ action, flowId, id, projectId, type, status, wait, flowName }) => {
1495
+ try {
1496
+ switch (action) {
1497
+ case "deploy": {
1498
+ if (!flowId) {
1499
+ return mcpError12(
1500
+ new Error(
1501
+ 'flowId is required for deploy action. Use flow_manage with action "list" to see available flows.'
1502
+ )
1503
+ );
1504
+ }
1505
+ const result = await deployFlow({
1506
+ flowId,
1507
+ wait: wait ?? true,
1508
+ flowName
1509
+ });
1510
+ return mcpResult12(result, {
1313
1511
  next: [
1314
- 'Use api(action: "deployment.list") to check current status'
1512
+ 'Use deploy_manage with action "get" to check deployment status'
1315
1513
  ]
1514
+ });
1515
+ }
1516
+ case "list": {
1517
+ const data = await listDeployments({ projectId, type, status });
1518
+ return mcpResult12(data);
1519
+ }
1520
+ case "get": {
1521
+ if (!id) {
1522
+ return mcpError12(
1523
+ new Error(
1524
+ 'id is required for get action. Use deploy_manage with action "list" to see available deployments.'
1525
+ )
1526
+ );
1316
1527
  }
1317
- );
1528
+ try {
1529
+ const data = await getDeploymentBySlug({ slug: id, projectId });
1530
+ return mcpResult12(data);
1531
+ } catch {
1532
+ const data = await getDeployment({ flowId: id, projectId });
1533
+ return mcpResult12(data);
1534
+ }
1535
+ }
1536
+ case "delete": {
1537
+ if (!id) {
1538
+ return mcpError12(
1539
+ new Error(
1540
+ 'id is required for delete action. Use deploy_manage with action "list" to see available deployments.'
1541
+ )
1542
+ );
1543
+ }
1544
+ try {
1545
+ const data = await deleteDeployment({ slug: id, projectId });
1546
+ return mcpResult12({ deleted: true, ...data });
1547
+ } catch (error) {
1548
+ if (isAuthError(error)) {
1549
+ return mcpError12(error, AUTH_HINT);
1550
+ }
1551
+ const message = error instanceof Error ? error.message : String(error);
1552
+ if (/not[\s_-]?found|404/i.test(message)) {
1553
+ return mcpError12(
1554
+ error,
1555
+ 'Deployment not found. The "delete" action expects a deployment slug (e.g. "dep_..."), not a flow ID. If you only have a flowId, use flow_manage with action "get" to resolve the latest deployment slug first.'
1556
+ );
1557
+ }
1558
+ return mcpError12(error);
1559
+ }
1560
+ }
1561
+ default:
1562
+ throw new Error(
1563
+ `Unknown action: ${action}. Use one of: deploy, list, get, delete`
1564
+ );
1318
1565
  }
1319
- if (msg.includes("401") || msg.includes("403") || msg.includes("Unauthorized"))
1320
- return mcpError9(
1321
- error,
1322
- "Set WALKEROS_TOKEN env var or check token expiry"
1323
- );
1324
- if (msg.includes("required"))
1325
- return mcpError9(
1326
- error,
1327
- `See api tool description for ${action} parameters.`
1328
- );
1329
- return mcpError9(error);
1566
+ } catch (error) {
1567
+ return mcpError12(error, isAuthError(error) ? AUTH_HINT : void 0);
1330
1568
  }
1331
1569
  }
1332
1570
  );
@@ -1554,8 +1792,8 @@ function registerReferenceResources(server2) {
1554
1792
  }
1555
1793
  );
1556
1794
  server2.resource(
1557
- "api",
1558
- "walkeros://reference/api",
1795
+ "openapi",
1796
+ "walkeros://reference/openapi",
1559
1797
  {
1560
1798
  description: "walkerOS cloud API \u2014 OpenAPI 3.1 specification",
1561
1799
  mimeType: "application/json"
@@ -1574,7 +1812,7 @@ function registerReferenceResources(server2) {
1574
1812
  return {
1575
1813
  contents: [
1576
1814
  {
1577
- uri: "walkeros://reference/api",
1815
+ uri: "walkeros://reference/openapi",
1578
1816
  text: openApiSpec,
1579
1817
  mimeType: "application/json"
1580
1818
  }
@@ -1605,17 +1843,17 @@ function registerReferenceResources(server2) {
1605
1843
  }
1606
1844
 
1607
1845
  // src/prompts/add-step.ts
1608
- import { z as z11 } from "zod";
1846
+ import { z as z13 } from "zod";
1609
1847
  function registerAddStepPrompt(server2) {
1610
1848
  server2.registerPrompt(
1611
1849
  "add-step",
1612
1850
  {
1613
1851
  description: "Add a source, destination, transformer, or store step to a flow configuration. Guides through package selection, config scaffolding, and wiring.",
1614
1852
  argsSchema: {
1615
- stepType: z11.string().optional().describe(
1853
+ stepType: z13.string().optional().describe(
1616
1854
  "Type of step to add: source, destination, transformer, or store"
1617
1855
  ),
1618
- flowPath: z11.string().optional().describe("Path to the flow.json file to modify")
1856
+ flowPath: z13.string().optional().describe("Path to the flow.json file to modify")
1619
1857
  }
1620
1858
  },
1621
1859
  async ({ stepType, flowPath }) => ({
@@ -1656,14 +1894,14 @@ function registerAddStepPrompt(server2) {
1656
1894
  }
1657
1895
 
1658
1896
  // src/prompts/setup-mapping.ts
1659
- import { z as z12 } from "zod";
1897
+ import { z as z14 } from "zod";
1660
1898
  function registerSetupMappingPrompt(server2) {
1661
1899
  server2.registerPrompt(
1662
1900
  "setup-mapping",
1663
1901
  {
1664
1902
  description: "Set up event mapping for any step in a flow. Teaches mapping syntax and uses package examples as templates.",
1665
1903
  argsSchema: {
1666
- stepName: z12.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
1904
+ stepName: z14.string().optional().describe('Step name in the flow (e.g., "gtag", "meta", "express")')
1667
1905
  }
1668
1906
  },
1669
1907
  async ({ stepName }) => ({
@@ -1701,14 +1939,14 @@ function registerSetupMappingPrompt(server2) {
1701
1939
  }
1702
1940
 
1703
1941
  // src/prompts/manage-contract.ts
1704
- import { z as z13 } from "zod";
1942
+ import { z as z15 } from "zod";
1705
1943
  function registerManageContractPrompt(server2) {
1706
1944
  server2.registerPrompt(
1707
1945
  "manage-contract",
1708
1946
  {
1709
1947
  description: "Create or update event contracts for a flow. Can generate contracts from existing mappings or scaffold mappings from contracts.",
1710
1948
  argsSchema: {
1711
- direction: z13.string().optional().describe(
1949
+ direction: z15.string().optional().describe(
1712
1950
  'Direction: "from-mappings" (extract contract from existing mappings), "from-scratch" (create new contract), or "to-mappings" (scaffold mappings from contract)'
1713
1951
  )
1714
1952
  }
@@ -1746,14 +1984,14 @@ function registerManageContractPrompt(server2) {
1746
1984
  }
1747
1985
 
1748
1986
  // src/prompts/use-definitions.ts
1749
- import { z as z14 } from "zod";
1987
+ import { z as z16 } from "zod";
1750
1988
  function registerUseDefinitionsPrompt(server2) {
1751
1989
  server2.registerPrompt(
1752
1990
  "use-definitions",
1753
1991
  {
1754
1992
  description: "Extract shared patterns into definitions and variables for DRY, environment-aware flow configurations.",
1755
1993
  argsSchema: {
1756
- flowPath: z14.string().optional().describe("Path to the flow.json file to analyze")
1994
+ flowPath: z16.string().optional().describe("Path to the flow.json file to analyze")
1757
1995
  }
1758
1996
  },
1759
1997
  async ({ flowPath }) => ({
@@ -1794,11 +2032,11 @@ function registerUseDefinitionsPrompt(server2) {
1794
2032
  }
1795
2033
 
1796
2034
  // src/index.ts
1797
- setClientContext({ type: "mcp", version: "3.4.1-next-1776790594143" });
2035
+ setClientContext({ type: "mcp", version: "3.4.2" });
1798
2036
  var server = new McpServer(
1799
2037
  {
1800
2038
  name: "walkeros-flow",
1801
- version: "3.4.1-next-1776790594143"
2039
+ version: "3.4.2"
1802
2040
  },
1803
2041
  {
1804
2042
  instructions: `walkerOS is an open-source, privacy-first event data collection platform. Define event pipelines as code using JSON flow configurations.
@@ -1812,15 +2050,18 @@ var server = new McpServer(
1812
2050
 
1813
2051
  ## Workflow
1814
2052
 
1815
- 1. \`flow_load({ platform: "web" })\` or \`flow_load({ source: "./flow.json" })\` \u2014 create or load
1816
- 2. \`package_search({ type: "destination", platform: "web" })\` \u2014 discover packages
1817
- 3. \`package_get({ package: "..." })\` \u2014 read schemas, hints, examples
1818
- 4. Use the \`add-step\` prompt \u2014 guided step addition
1819
- 5. Use the \`setup-mapping\` prompt \u2014 event transformation config
1820
- 6. \`flow_validate({ type: "flow", input: "flow.json" })\` \u2014 verify
1821
- 7. \`flow_simulate({ configPath: "flow.json", event: "..." })\` \u2014 test
1822
- 8. \`flow_bundle({ configPath: "flow.json" })\` \u2014 build
1823
- 9. \`api({ action: "deploy", id: "cfg_..." })\` \u2014 deploy (requires WALKEROS_TOKEN)
2053
+ 1. \`auth({ action: "status" })\` \u2014 check if logged in, or \`auth({ action: "login" })\` to connect
2054
+ 2. \`project_manage({ action: "list" })\` \u2014 see your projects
2055
+ 3. \`flow_manage({ action: "list" })\` \u2014 see all flows across projects
2056
+ 4. \`flow_load({ platform: "web" })\` or \`flow_load({ source: "./flow.json" })\` \u2014 create or load
2057
+ 5. \`package_search({ type: "destination", platform: "web" })\` \u2014 discover packages
2058
+ 6. \`package_get({ package: "..." })\` \u2014 read schemas, hints, examples
2059
+ 7. Use the \`add-step\` prompt \u2014 guided step addition
2060
+ 8. Use the \`setup-mapping\` prompt \u2014 event transformation config
2061
+ 9. \`flow_validate({ type: "flow", input: "flow.json" })\` \u2014 verify
2062
+ 10. \`flow_simulate({ configPath: "flow.json", event: "..." })\` \u2014 test
2063
+ 11. \`flow_manage({ action: "update", flowId: "...", content: {...} })\` \u2014 save to cloud
2064
+ 12. \`deploy_manage({ action: "deploy", flowId: "..." })\` \u2014 deploy
1824
2065
 
1825
2066
  ## Architecture: Source \u2192 Collector \u2192 Destination(s)
1826
2067
 
@@ -1874,15 +2115,16 @@ registerPackageSearchTool(server);
1874
2115
  registerGetPackageSchemaTool(server);
1875
2116
  registerFlowLoadTool(server);
1876
2117
  registerFeedbackTool(server);
2118
+ registerAuthTool(server);
2119
+ registerProjectManageTool(server);
2120
+ registerFlowManageTool(server);
2121
+ registerDeployTool(server);
1877
2122
  registerPackageSchemaResources(server);
1878
2123
  registerReferenceResources(server);
1879
2124
  registerAddStepPrompt(server);
1880
2125
  registerSetupMappingPrompt(server);
1881
2126
  registerManageContractPrompt(server);
1882
2127
  registerUseDefinitionsPrompt(server);
1883
- if (process.env.WALKEROS_TOKEN) {
1884
- registerApiTool(server);
1885
- }
1886
2128
  async function main() {
1887
2129
  const transport = new StdioServerTransport();
1888
2130
  await server.connect(transport);