powerautomate-mcp 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1518,6 +1518,30 @@ var FlowManagementApi = class {
1518
1518
  return data;
1519
1519
  }, "Fetch resource link");
1520
1520
  }
1521
+ async getFlowRunActionRepetitions(flowId, runId, actionName, environmentId) {
1522
+ const envId = environmentId ?? this.defaultEnvironmentId;
1523
+ const safeFlowId = validateRowId(flowId);
1524
+ const safeRunId = validateFlowRunId(runId);
1525
+ const safeActionName = encodeURIComponent(actionName);
1526
+ apiLogger.debug({ flowId: safeFlowId, runId: safeRunId, actionName }, "Getting action repetitions");
1527
+ const url = this.buildUrl(
1528
+ envId,
1529
+ `/flows/${safeFlowId}/runs/${safeRunId}/actions/${safeActionName}/repetitions`
1530
+ );
1531
+ const headers = await this.getAuthHeaders();
1532
+ return wrapApiCall(async () => {
1533
+ const response = await request(url, { method: "GET", headers });
1534
+ const data = await this.handleResponse(
1535
+ response,
1536
+ `Get repetitions for action ${actionName}`
1537
+ );
1538
+ apiLogger.info(
1539
+ { flowId, runId, actionName, count: data.value.length },
1540
+ "Retrieved action repetitions"
1541
+ );
1542
+ return data.value;
1543
+ }, `Get repetitions for action ${actionName}`);
1544
+ }
1521
1545
  // ==========================================================================
1522
1546
  // Flow Sharing & Permissions
1523
1547
  // ==========================================================================
@@ -6872,11 +6896,14 @@ var getRunActionsInputSchema = z.object({
6872
6896
  flowId: z.string().describe("The flow ID (GUID)"),
6873
6897
  runId: z.string().describe("The run ID (GUID)"),
6874
6898
  environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
6875
- failedOnly: z.boolean().optional().describe("If true, only return failed actions (default: false)")
6899
+ failedOnly: z.boolean().optional().describe("If true, only return failed actions (default: false)"),
6900
+ includeInputs: z.boolean().optional().describe("If true, fetch and display input data for each action (default: false for succeeded, true for failed)"),
6901
+ includeOutputs: z.boolean().optional().describe("If true, fetch and display output data for each action (default: false for succeeded, true for failed)"),
6902
+ actionName: z.string().optional().describe("Filter to a specific action by name (partial match). Use to drill into one action's details.")
6876
6903
  });
6877
6904
  var getRunActionsTool = {
6878
6905
  name: "get_run_actions",
6879
- description: "Get detailed action-level information for a flow run. Shows each action's status, timing, and most importantly - error details for failed actions. Essential for debugging why a flow failed. Use 'failedOnly: true' to focus on just the failures.",
6906
+ description: "Get detailed action-level information for a flow run. Shows each action's status, timing, inputs/outputs, and error details. Use 'failedOnly: true' to focus on failures, 'actionName' to drill into a specific action, 'includeInputs'/'includeOutputs' to see full data payloads.",
6880
6907
  inputSchema: {
6881
6908
  type: "object",
6882
6909
  properties: {
@@ -6895,11 +6922,34 @@ var getRunActionsTool = {
6895
6922
  failedOnly: {
6896
6923
  type: "boolean",
6897
6924
  description: "If true, only return failed actions (default: false)"
6925
+ },
6926
+ includeInputs: {
6927
+ type: "boolean",
6928
+ description: "If true, fetch and display input data for each action (default: false for succeeded, true for failed)"
6929
+ },
6930
+ includeOutputs: {
6931
+ type: "boolean",
6932
+ description: "If true, fetch and display output data for each action (default: false for succeeded, true for failed)"
6933
+ },
6934
+ actionName: {
6935
+ type: "string",
6936
+ description: "Filter to a specific action by name (partial match). Use to drill into one action's details."
6898
6937
  }
6899
6938
  },
6900
6939
  required: ["flowId", "runId"]
6901
6940
  }
6902
6941
  };
6942
+ var TRUNCATE_LIMIT = 1e3;
6943
+ var ERROR_TRUNCATE_LIMIT = 500;
6944
+ function formatResourceData(data) {
6945
+ if (data && typeof data === "object" && "body" in data) {
6946
+ const body = data.body;
6947
+ const str2 = typeof body === "object" ? JSON.stringify(body, null, 2) : String(body);
6948
+ return str2.length > TRUNCATE_LIMIT ? str2.slice(0, TRUNCATE_LIMIT) + "\u2026" : str2;
6949
+ }
6950
+ const str = JSON.stringify(data, null, 2);
6951
+ return str.length > TRUNCATE_LIMIT ? str.slice(0, TRUNCATE_LIMIT) + "\u2026" : str;
6952
+ }
6903
6953
  async function handleGetRunActions(api, input) {
6904
6954
  const parsed = getRunActionsInputSchema.parse(input);
6905
6955
  logger.info({ input: parsed }, "Executing get_run_actions");
@@ -6909,38 +6959,70 @@ async function handleGetRunActions(api, input) {
6909
6959
  parsed.environment
6910
6960
  );
6911
6961
  let filteredActions = actions;
6962
+ if (parsed.actionName) {
6963
+ const needle = parsed.actionName.toLowerCase();
6964
+ filteredActions = filteredActions.filter(
6965
+ (a) => a.name.toLowerCase().includes(needle)
6966
+ );
6967
+ }
6912
6968
  if (parsed.failedOnly) {
6913
- filteredActions = actions.filter(
6969
+ filteredActions = filteredActions.filter(
6914
6970
  (a) => a.properties.status === "Failed" || a.properties.status === "TimedOut"
6915
6971
  );
6916
6972
  }
6917
- const keyActionNames = ["Result", "Debug_Sample", "Debug_F1", "Final_Count"];
6918
- const outputsToFetch = [];
6973
+ const shouldFetchInputs = (status) => {
6974
+ if (parsed.includeInputs === true) return true;
6975
+ if (parsed.includeInputs === false) return false;
6976
+ return status === "Failed" || status === "TimedOut";
6977
+ };
6978
+ const shouldFetchOutputs = (status) => {
6979
+ if (parsed.includeOutputs === true) return true;
6980
+ if (parsed.includeOutputs === false) return false;
6981
+ return status === "Failed" || status === "TimedOut";
6982
+ };
6983
+ const actionIO = {};
6984
+ const fetchPromises = [];
6919
6985
  for (const action of filteredActions) {
6920
- const isKeyAction = keyActionNames.some((k) => action.name.includes(k) || action.name === k);
6921
- if (isKeyAction && action.properties.outputsLink?.uri) {
6922
- outputsToFetch.push({ name: action.name, uri: action.properties.outputsLink.uri });
6986
+ const status = action.properties.status;
6987
+ const needInputs = shouldFetchInputs(status) && action.properties.inputsLink?.uri;
6988
+ const needOutputs = shouldFetchOutputs(status) && action.properties.outputsLink?.uri;
6989
+ if (needInputs) {
6990
+ fetchPromises.push(
6991
+ api.fetchResourceLink(action.properties.inputsLink.uri).then(
6992
+ (data) => {
6993
+ if (!actionIO[action.name]) actionIO[action.name] = {};
6994
+ actionIO[action.name].inputs = formatResourceData(data);
6995
+ },
6996
+ (err) => {
6997
+ if (!actionIO[action.name]) actionIO[action.name] = {};
6998
+ const msg = err instanceof Error ? err.message : String(err);
6999
+ actionIO[action.name].inputs = `(fetch error: ${msg.slice(0, 100)})`;
7000
+ }
7001
+ )
7002
+ );
6923
7003
  }
6924
- }
6925
- const actionOutputs = {};
6926
- for (const { name, uri } of outputsToFetch) {
6927
- try {
6928
- const output = await api.fetchResourceLink(uri);
6929
- if (output && typeof output === "object" && "body" in output) {
6930
- const body = output.body;
6931
- actionOutputs[name] = typeof body === "object" ? JSON.stringify(body).slice(0, 200) : String(body);
6932
- } else {
6933
- actionOutputs[name] = JSON.stringify(output).slice(0, 200);
6934
- }
6935
- } catch (err) {
6936
- const msg = err instanceof Error ? err.message : String(err);
6937
- actionOutputs[name] = `(error: ${msg.slice(0, 50)})`;
7004
+ if (needOutputs) {
7005
+ fetchPromises.push(
7006
+ api.fetchResourceLink(action.properties.outputsLink.uri).then(
7007
+ (data) => {
7008
+ if (!actionIO[action.name]) actionIO[action.name] = {};
7009
+ actionIO[action.name].outputs = formatResourceData(data);
7010
+ },
7011
+ (err) => {
7012
+ if (!actionIO[action.name]) actionIO[action.name] = {};
7013
+ const msg = err instanceof Error ? err.message : String(err);
7014
+ actionIO[action.name].outputs = `(fetch error: ${msg.slice(0, 100)})`;
7015
+ }
7016
+ )
7017
+ );
6938
7018
  }
6939
7019
  }
6940
- const result = filteredActions.map((action) => {
7020
+ await Promise.all(fetchPromises);
7021
+ const results = filteredActions.map((action) => {
6941
7022
  const props = action.properties;
6942
7023
  const startTime = new Date(props.startTime);
6943
7024
  const endTime = props.endTime ? new Date(props.endTime) : void 0;
7025
+ const io = actionIO[action.name];
6944
7026
  return {
6945
7027
  name: action.name,
6946
7028
  status: props.status,
@@ -6948,25 +7030,213 @@ async function handleGetRunActions(api, input) {
6948
7030
  endTime: props.endTime,
6949
7031
  durationMs: endTime ? endTime.getTime() - startTime.getTime() : void 0,
6950
7032
  error: props.error,
6951
- retryCount: props.retryHistory?.length
7033
+ inputs: io?.inputs,
7034
+ outputs: io?.outputs,
7035
+ retryCount: props.retryHistory?.length,
7036
+ trackedProperties: props.trackedProperties
6952
7037
  };
6953
7038
  });
6954
- const failedCount = result.filter((r) => r.status === "Failed" || r.status === "TimedOut").length;
6955
- const succeededCount = result.filter((r) => r.status === "Succeeded").length;
6956
- const lines = [`Actions: ${result.length} total (${succeededCount} succeeded, ${failedCount} failed)`];
6957
- if (Object.keys(actionOutputs).length > 0) {
6958
- lines.push("\nOutputs:");
6959
- for (const [name, value] of Object.entries(actionOutputs)) {
6960
- lines.push(` ${name}: ${value}`);
6961
- }
6962
- lines.push("");
7039
+ const failedCount = results.filter(
7040
+ (r) => r.status === "Failed" || r.status === "TimedOut"
7041
+ ).length;
7042
+ const succeededCount = results.filter(
7043
+ (r) => r.status === "Succeeded"
7044
+ ).length;
7045
+ const lines = [
7046
+ `Actions: ${results.length} total (${succeededCount} succeeded, ${failedCount} failed)`
7047
+ ];
7048
+ if (parsed.actionName) {
7049
+ lines[0] += ` [filtered by "${parsed.actionName}"]`;
6963
7050
  }
6964
- for (const action of result) {
7051
+ for (const action of results) {
6965
7052
  const duration = action.durationMs ? `${(action.durationMs / 1e3).toFixed(1)}s` : "";
6966
- const status = action.status === "Succeeded" ? "\u2713" : action.status === "Failed" ? "\u2717" : "\u25CB";
6967
- let line = ` ${status} ${action.name} ${duration}`;
7053
+ const status = action.status === "Succeeded" ? "\u2713" : action.status === "Failed" ? "\u2717" : action.status === "TimedOut" ? "\u23F1" : "\u25CB";
7054
+ let line = `
7055
+ ${status} ${action.name} ${duration}`;
6968
7056
  if (action.error) {
6969
- line += ` - ${action.error.message.slice(0, 100)}`;
7057
+ line += `
7058
+ Error: [${action.error.code}] ${action.error.message.slice(0, ERROR_TRUNCATE_LIMIT)}`;
7059
+ }
7060
+ if (action.inputs) {
7061
+ line += `
7062
+ Inputs: ${action.inputs}`;
7063
+ }
7064
+ if (action.outputs) {
7065
+ line += `
7066
+ Outputs: ${action.outputs}`;
7067
+ }
7068
+ if (action.trackedProperties && Object.keys(action.trackedProperties).length > 0) {
7069
+ line += `
7070
+ Tracked: ${JSON.stringify(action.trackedProperties)}`;
7071
+ }
7072
+ if (action.retryCount && action.retryCount > 0) {
7073
+ line += `
7074
+ Retries: ${action.retryCount}`;
7075
+ }
7076
+ lines.push(line);
7077
+ }
7078
+ return lines.join("\n");
7079
+ }
7080
+ var getRunActionRepetitionsInputSchema = z.object({
7081
+ flowId: z.string().describe("The flow ID (GUID)"),
7082
+ runId: z.string().describe("The run ID (GUID)"),
7083
+ actionName: z.string().describe("The exact action name inside the for_each/do_until loop"),
7084
+ environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
7085
+ failedOnly: z.boolean().optional().describe("If true, only return failed iterations (default: false)"),
7086
+ includeInputs: z.boolean().optional().describe("If true, fetch input data for each iteration (default: false)"),
7087
+ includeOutputs: z.boolean().optional().describe("If true, fetch output data for each iteration (default: true for failed)"),
7088
+ maxIterations: z.number().optional().describe("Maximum iterations to return (default: 50). Use to limit large loops.")
7089
+ });
7090
+ var getRunActionRepetitionsTool = {
7091
+ name: "get_run_action_repetitions",
7092
+ description: "Get iteration-level details for a for_each or do_until loop action in a flow run. Shows which iterations succeeded/failed, their inputs/outputs, and error details. Essential for debugging loops that partially fail.",
7093
+ inputSchema: {
7094
+ type: "object",
7095
+ properties: {
7096
+ flowId: {
7097
+ type: "string",
7098
+ description: "The flow ID (GUID)"
7099
+ },
7100
+ runId: {
7101
+ type: "string",
7102
+ description: "The run ID (GUID)"
7103
+ },
7104
+ actionName: {
7105
+ type: "string",
7106
+ description: "The exact action name inside the for_each/do_until loop"
7107
+ },
7108
+ environment: {
7109
+ type: "string",
7110
+ description: "Environment name or GUID. Uses default if omitted."
7111
+ },
7112
+ failedOnly: {
7113
+ type: "boolean",
7114
+ description: "If true, only return failed iterations (default: false)"
7115
+ },
7116
+ includeInputs: {
7117
+ type: "boolean",
7118
+ description: "If true, fetch input data for each iteration (default: false)"
7119
+ },
7120
+ includeOutputs: {
7121
+ type: "boolean",
7122
+ description: "If true, fetch output data for each iteration (default: true for failed)"
7123
+ },
7124
+ maxIterations: {
7125
+ type: "number",
7126
+ description: "Maximum iterations to return (default: 50). Use to limit large loops."
7127
+ }
7128
+ },
7129
+ required: ["flowId", "runId", "actionName"]
7130
+ }
7131
+ };
7132
+ var TRUNCATE_LIMIT2 = 1e3;
7133
+ function formatData(data) {
7134
+ if (data && typeof data === "object" && "body" in data) {
7135
+ const body = data.body;
7136
+ const str2 = typeof body === "object" ? JSON.stringify(body, null, 2) : String(body);
7137
+ return str2.length > TRUNCATE_LIMIT2 ? str2.slice(0, TRUNCATE_LIMIT2) + "\u2026" : str2;
7138
+ }
7139
+ const str = JSON.stringify(data, null, 2);
7140
+ return str.length > TRUNCATE_LIMIT2 ? str.slice(0, TRUNCATE_LIMIT2) + "\u2026" : str;
7141
+ }
7142
+ async function handleGetRunActionRepetitions(api, input) {
7143
+ const parsed = getRunActionRepetitionsInputSchema.parse(input);
7144
+ const maxIter = parsed.maxIterations ?? 50;
7145
+ logger.info({ input: parsed }, "Executing get_run_action_repetitions");
7146
+ const repetitions = await api.getFlowRunActionRepetitions(
7147
+ parsed.flowId,
7148
+ parsed.runId,
7149
+ parsed.actionName,
7150
+ parsed.environment
7151
+ );
7152
+ let filtered = repetitions;
7153
+ if (parsed.failedOnly) {
7154
+ filtered = filtered.filter(
7155
+ (r) => r.properties.status === "Failed" || r.properties.status === "TimedOut"
7156
+ );
7157
+ }
7158
+ const totalCount = filtered.length;
7159
+ filtered = filtered.slice(0, maxIter);
7160
+ const shouldFetchInputs = (status) => {
7161
+ if (parsed.includeInputs === true) return true;
7162
+ return false;
7163
+ };
7164
+ const shouldFetchOutputs = (status) => {
7165
+ if (parsed.includeOutputs === true) return true;
7166
+ if (parsed.includeOutputs === false) return false;
7167
+ return status === "Failed" || status === "TimedOut";
7168
+ };
7169
+ const iterIO = {};
7170
+ const fetchPromises = [];
7171
+ for (const rep of filtered) {
7172
+ const status = rep.properties.status;
7173
+ const key = rep.name;
7174
+ if (shouldFetchInputs() && rep.properties.inputsLink?.uri) {
7175
+ fetchPromises.push(
7176
+ api.fetchResourceLink(rep.properties.inputsLink.uri).then(
7177
+ (data) => {
7178
+ if (!iterIO[key]) iterIO[key] = {};
7179
+ iterIO[key].inputs = formatData(data);
7180
+ },
7181
+ (err) => {
7182
+ if (!iterIO[key]) iterIO[key] = {};
7183
+ const msg = err instanceof Error ? err.message : String(err);
7184
+ iterIO[key].inputs = `(fetch error: ${msg.slice(0, 100)})`;
7185
+ }
7186
+ )
7187
+ );
7188
+ }
7189
+ if (shouldFetchOutputs(status) && rep.properties.outputsLink?.uri) {
7190
+ fetchPromises.push(
7191
+ api.fetchResourceLink(rep.properties.outputsLink.uri).then(
7192
+ (data) => {
7193
+ if (!iterIO[key]) iterIO[key] = {};
7194
+ iterIO[key].outputs = formatData(data);
7195
+ },
7196
+ (err) => {
7197
+ if (!iterIO[key]) iterIO[key] = {};
7198
+ const msg = err instanceof Error ? err.message : String(err);
7199
+ iterIO[key].outputs = `(fetch error: ${msg.slice(0, 100)})`;
7200
+ }
7201
+ )
7202
+ );
7203
+ }
7204
+ }
7205
+ await Promise.all(fetchPromises);
7206
+ const failedCount = filtered.filter(
7207
+ (r) => r.properties.status === "Failed" || r.properties.status === "TimedOut"
7208
+ ).length;
7209
+ const succeededCount = filtered.filter(
7210
+ (r) => r.properties.status === "Succeeded"
7211
+ ).length;
7212
+ const lines = [
7213
+ `Repetitions for "${parsed.actionName}": ${totalCount} total (${succeededCount} succeeded, ${failedCount} failed)`
7214
+ ];
7215
+ if (totalCount > maxIter) {
7216
+ lines.push(` (showing first ${maxIter} of ${totalCount})`);
7217
+ }
7218
+ for (const rep of filtered) {
7219
+ const props = rep.properties;
7220
+ const startTime = new Date(props.startTime);
7221
+ const endTime = props.endTime ? new Date(props.endTime) : void 0;
7222
+ const durationMs = endTime ? endTime.getTime() - startTime.getTime() : void 0;
7223
+ const duration = durationMs ? `${(durationMs / 1e3).toFixed(1)}s` : "";
7224
+ const indexes = props.repetitionIndexes?.map((ri) => `${ri.scopeName}[${ri.itemIndex}]`).join(" > ");
7225
+ const status = props.status === "Succeeded" ? "\u2713" : props.status === "Failed" ? "\u2717" : props.status === "TimedOut" ? "\u23F1" : "\u25CB";
7226
+ let line = `
7227
+ ${status} ${indexes ?? rep.name} ${duration}`;
7228
+ if (props.error) {
7229
+ line += `
7230
+ Error: [${props.error.code}] ${props.error.message.slice(0, 500)}`;
7231
+ }
7232
+ const io = iterIO[rep.name];
7233
+ if (io?.inputs) {
7234
+ line += `
7235
+ Inputs: ${io.inputs}`;
7236
+ }
7237
+ if (io?.outputs) {
7238
+ line += `
7239
+ Outputs: ${io.outputs}`;
6970
7240
  }
6971
7241
  lines.push(line);
6972
7242
  }
@@ -12923,6 +13193,7 @@ var tools = [
12923
13193
  // Phase 4: Connections, Diagnostics, Sharing
12924
13194
  listConnectionsTool,
12925
13195
  getRunActionsTool,
13196
+ getRunActionRepetitionsTool,
12926
13197
  getFlowPermissionsTool,
12927
13198
  shareFlowTool,
12928
13199
  // Phase 5: Run Control, Environment Management
@@ -13058,6 +13329,9 @@ async function executeTool(context, toolName, input) {
13058
13329
  case "get_run_actions":
13059
13330
  result = await handleGetRunActions(context.flowApi, input);
13060
13331
  break;
13332
+ case "get_run_action_repetitions":
13333
+ result = await handleGetRunActionRepetitions(context.flowApi, input);
13334
+ break;
13061
13335
  case "get_flow_permissions":
13062
13336
  result = await handleGetFlowPermissions(context.flowApi, input);
13063
13337
  break;
@@ -15045,6 +15319,7 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
15045
15319
  };
15046
15320
  const pca = new PublicClientApplication(msalConfig);
15047
15321
  let currentAccount = null;
15322
+ let interactiveAuthCompleted = false;
15048
15323
  const pendingTokenRequests = /* @__PURE__ */ new Map();
15049
15324
  currentAccount = loadAccount();
15050
15325
  if (!currentAccount) {
@@ -15091,7 +15366,7 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
15091
15366
  "No cached credentials available. Run 'powerautomate-mcp --setup' first to authenticate."
15092
15367
  );
15093
15368
  }
15094
- if (currentAccount) {
15369
+ if (currentAccount && interactiveAuthCompleted) {
15095
15370
  throw new AuthenticationError(
15096
15371
  `Silent token acquisition failed for scopes: ${mutableScopes.join(", ")}. Resource may not be registered in the app registration. Re-run --setup to update permissions.`
15097
15372
  );
@@ -15141,6 +15416,7 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
15141
15416
  currentAccount = result.account;
15142
15417
  saveAccount(result.account);
15143
15418
  }
15419
+ interactiveAuthCompleted = true;
15144
15420
  logger.info({ username: result.account?.username }, "Device code authentication succeeded");
15145
15421
  return result;
15146
15422
  } catch (err) {