powerautomate-mcp 0.5.3 → 0.7.0
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 +612 -110
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync, lstatSync, statSync,
|
|
2
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, lstatSync, statSync, unlinkSync, chmodSync, readdirSync } from 'fs';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { platform, env } from 'process';
|
|
5
5
|
import { homedir } from 'os';
|
|
@@ -381,6 +381,7 @@ Create the file with your Microsoft Entra app client ID and Power Automate envir
|
|
|
381
381
|
err
|
|
382
382
|
);
|
|
383
383
|
}
|
|
384
|
+
parsed = interpolateEnvVars(parsed);
|
|
384
385
|
const envClientId = env["PA_MCP_CLIENT_ID"];
|
|
385
386
|
if (envClientId) {
|
|
386
387
|
const UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -423,12 +424,34 @@ function resolveEnvironment(config, envNameOrId) {
|
|
|
423
424
|
displayName: envConfig.displayName
|
|
424
425
|
};
|
|
425
426
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
427
|
+
{
|
|
428
|
+
throw new ConfigurationError(
|
|
429
|
+
`Environment "${envKey}" not found in configuration. Available environments: ${Object.keys(config.environments).filter((k) => k !== "default").join(", ") || "(none)"}`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
function interpolateEnvVars(value) {
|
|
434
|
+
if (typeof value === "string") {
|
|
435
|
+
return value.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
436
|
+
const envValue = env[varName];
|
|
437
|
+
if (envValue === void 0) {
|
|
438
|
+
logger.warn({ varName }, "Environment variable referenced in config is not set");
|
|
439
|
+
return "";
|
|
440
|
+
}
|
|
441
|
+
return envValue;
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (Array.isArray(value)) {
|
|
445
|
+
return value.map(interpolateEnvVars);
|
|
446
|
+
}
|
|
447
|
+
if (value !== null && typeof value === "object") {
|
|
448
|
+
const result = {};
|
|
449
|
+
for (const [key, val] of Object.entries(value)) {
|
|
450
|
+
result[key] = interpolateEnvVars(val);
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
return value;
|
|
432
455
|
}
|
|
433
456
|
|
|
434
457
|
// src/utils/security.ts
|
|
@@ -1126,12 +1149,12 @@ var FlowManagementApi = class {
|
|
|
1126
1149
|
throw new ApiError(`${context}: API error`, statusCode, errorDetails);
|
|
1127
1150
|
}
|
|
1128
1151
|
if (!responseText) {
|
|
1129
|
-
return {};
|
|
1152
|
+
return { value: [] };
|
|
1130
1153
|
}
|
|
1131
1154
|
return JSON.parse(responseText);
|
|
1132
1155
|
}
|
|
1133
1156
|
/**
|
|
1134
|
-
* List flows in an environment
|
|
1157
|
+
* List flows in an environment with optional pagination support
|
|
1135
1158
|
*/
|
|
1136
1159
|
async listFlows(options = {}) {
|
|
1137
1160
|
const envId = options.environment ?? this.defaultEnvironmentId;
|
|
@@ -1154,8 +1177,8 @@ var FlowManagementApi = class {
|
|
|
1154
1177
|
response,
|
|
1155
1178
|
"List flows"
|
|
1156
1179
|
);
|
|
1157
|
-
apiLogger.info({ count: data.value.length }, "Listed flows");
|
|
1158
|
-
return data.value;
|
|
1180
|
+
apiLogger.info({ count: data.value.length, hasMore: !!data.nextLink }, "Listed flows");
|
|
1181
|
+
return { flows: data.value, nextLink: data.nextLink };
|
|
1159
1182
|
}, "List flows");
|
|
1160
1183
|
}
|
|
1161
1184
|
/**
|
|
@@ -1448,9 +1471,13 @@ var FlowManagementApi = class {
|
|
|
1448
1471
|
const safeFlowId = validateRowId(flowId);
|
|
1449
1472
|
const safeRunId = validateRowId(runId);
|
|
1450
1473
|
apiLogger.info({ flowId: safeFlowId, runId: safeRunId, envId }, "Resubmitting flow run");
|
|
1474
|
+
const flow = await this.getFlow(safeFlowId, envId);
|
|
1475
|
+
const triggers = flow.properties.definition?.triggers ?? {};
|
|
1476
|
+
const triggerName = Object.keys(triggers)[0] ?? "manual";
|
|
1477
|
+
apiLogger.debug({ flowId: safeFlowId, triggerName }, "Using trigger name for resubmit");
|
|
1451
1478
|
const url = this.buildUrl(
|
|
1452
1479
|
envId,
|
|
1453
|
-
`/flows/${safeFlowId}/triggers/
|
|
1480
|
+
`/flows/${safeFlowId}/triggers/${triggerName}/histories/${safeRunId}/resubmit`
|
|
1454
1481
|
);
|
|
1455
1482
|
const headers = await this.getAuthHeaders();
|
|
1456
1483
|
return wrapApiCall(async () => {
|
|
@@ -1549,7 +1576,7 @@ var FlowManagementApi = class {
|
|
|
1549
1576
|
*/
|
|
1550
1577
|
async listApprovals(environmentId, top) {
|
|
1551
1578
|
const envId = environmentId ?? this.defaultEnvironmentId;
|
|
1552
|
-
const limit = top ?? 25;
|
|
1579
|
+
const limit = Math.min(top ?? 25, MAX_PAGE_SIZE);
|
|
1553
1580
|
apiLogger.debug({ envId, limit }, "Listing approvals");
|
|
1554
1581
|
const url = this.buildUrl(envId, "/approvals", { $top: limit });
|
|
1555
1582
|
const headers = await this.getAuthHeaders();
|
|
@@ -1631,6 +1658,12 @@ var ConnectorMetadataApi = class {
|
|
|
1631
1658
|
*/
|
|
1632
1659
|
async handleResponse(response, context) {
|
|
1633
1660
|
const { statusCode, body } = response;
|
|
1661
|
+
if (statusCode === 429) {
|
|
1662
|
+
await body.text();
|
|
1663
|
+
const retryAfter = response.headers["retry-after"];
|
|
1664
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
1665
|
+
throw new RateLimitError(retryMs);
|
|
1666
|
+
}
|
|
1634
1667
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
1635
1668
|
const chunks = [];
|
|
1636
1669
|
let totalSize = 0;
|
|
@@ -1965,6 +1998,12 @@ var GraphApi = class {
|
|
|
1965
1998
|
*/
|
|
1966
1999
|
async handleBinaryResponse(response, context) {
|
|
1967
2000
|
const { statusCode, body, headers } = response;
|
|
2001
|
+
if (statusCode === 429) {
|
|
2002
|
+
await body.text();
|
|
2003
|
+
const retryAfter = response.headers["retry-after"];
|
|
2004
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
2005
|
+
throw new RateLimitError(retryMs);
|
|
2006
|
+
}
|
|
1968
2007
|
if (statusCode >= 400) {
|
|
1969
2008
|
const responseText = await body.text();
|
|
1970
2009
|
graphLogger.error({ statusCode, context }, "Graph API binary response error");
|
|
@@ -1993,6 +2032,12 @@ var GraphApi = class {
|
|
|
1993
2032
|
}
|
|
1994
2033
|
async handleResponse(response, context) {
|
|
1995
2034
|
const { statusCode, body } = response;
|
|
2035
|
+
if (statusCode === 429) {
|
|
2036
|
+
await body.text();
|
|
2037
|
+
const retryAfter = response.headers["retry-after"];
|
|
2038
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
2039
|
+
throw new RateLimitError(retryMs);
|
|
2040
|
+
}
|
|
1996
2041
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
1997
2042
|
const chunks = [];
|
|
1998
2043
|
let totalSize = 0;
|
|
@@ -2015,7 +2060,7 @@ var GraphApi = class {
|
|
|
2015
2060
|
throw new ApiError(`${context}: Graph API error`, statusCode, errorDetails);
|
|
2016
2061
|
}
|
|
2017
2062
|
if (!responseText) {
|
|
2018
|
-
return {};
|
|
2063
|
+
return { value: [] };
|
|
2019
2064
|
}
|
|
2020
2065
|
return JSON.parse(responseText);
|
|
2021
2066
|
}
|
|
@@ -3262,6 +3307,12 @@ var DataverseApi = class {
|
|
|
3262
3307
|
*/
|
|
3263
3308
|
async handleResponse(response, context) {
|
|
3264
3309
|
const { statusCode, body } = response;
|
|
3310
|
+
if (statusCode === 429) {
|
|
3311
|
+
await body.text();
|
|
3312
|
+
const retryAfter = response.headers["retry-after"];
|
|
3313
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
3314
|
+
throw new RateLimitError(retryMs);
|
|
3315
|
+
}
|
|
3265
3316
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
3266
3317
|
const chunks = [];
|
|
3267
3318
|
let totalSize = 0;
|
|
@@ -3287,7 +3338,7 @@ var DataverseApi = class {
|
|
|
3287
3338
|
throw new ApiError(`${context}: API error`, statusCode, errorDetails);
|
|
3288
3339
|
}
|
|
3289
3340
|
if (!responseText) {
|
|
3290
|
-
return {};
|
|
3341
|
+
return { value: [] };
|
|
3291
3342
|
}
|
|
3292
3343
|
return JSON.parse(responseText);
|
|
3293
3344
|
}
|
|
@@ -4080,6 +4131,7 @@ var SchemaCache = class {
|
|
|
4080
4131
|
};
|
|
4081
4132
|
var listFlowsInputSchema = z.object({
|
|
4082
4133
|
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
4134
|
+
search: z.string().optional().describe("Search flows by display name (case-insensitive substring match). Applied client-side after fetching."),
|
|
4083
4135
|
filter: z.string().optional().describe(`OData filter expression (e.g., "properties/state eq 'Started'")`),
|
|
4084
4136
|
top: z.number().min(1).max(250).optional().describe("Maximum number of results to return (default 50, max 250)")
|
|
4085
4137
|
});
|
|
@@ -4093,6 +4145,10 @@ var listFlowsTool = {
|
|
|
4093
4145
|
type: "string",
|
|
4094
4146
|
description: "Environment name or GUID. Uses default if omitted."
|
|
4095
4147
|
},
|
|
4148
|
+
search: {
|
|
4149
|
+
type: "string",
|
|
4150
|
+
description: "Search flows by display name (case-insensitive substring match)."
|
|
4151
|
+
},
|
|
4096
4152
|
filter: {
|
|
4097
4153
|
type: "string",
|
|
4098
4154
|
description: `OData filter expression (e.g., "properties/state eq 'Started'")`
|
|
@@ -4117,11 +4173,16 @@ async function handleListFlows(api, input) {
|
|
|
4117
4173
|
return `Invalid filter: ${err instanceof Error ? err.message : "Filter contains disallowed patterns"}`;
|
|
4118
4174
|
}
|
|
4119
4175
|
}
|
|
4120
|
-
const
|
|
4176
|
+
const result = await api.listFlows({
|
|
4121
4177
|
environment: parsed.environment,
|
|
4122
4178
|
filter: safeFilter,
|
|
4123
4179
|
top: parsed.top ?? 50
|
|
4124
4180
|
});
|
|
4181
|
+
let flows = result.flows;
|
|
4182
|
+
if (parsed.search) {
|
|
4183
|
+
const needle = parsed.search.toLowerCase();
|
|
4184
|
+
flows = flows.filter((f) => f.properties.displayName.toLowerCase().includes(needle));
|
|
4185
|
+
}
|
|
4125
4186
|
if (flows.length === 0) {
|
|
4126
4187
|
return "No flows found";
|
|
4127
4188
|
}
|
|
@@ -4131,6 +4192,10 @@ async function handleListFlows(api, input) {
|
|
|
4131
4192
|
const modified = new Date(flow.properties.lastModifiedTime).toLocaleDateString();
|
|
4132
4193
|
lines.push(` ${state} ${flow.properties.displayName} (${flow.name}) - ${modified}`);
|
|
4133
4194
|
}
|
|
4195
|
+
if (result.nextLink) {
|
|
4196
|
+
lines.push(`
|
|
4197
|
+
(More flows available \u2014 use skipToken for next page)`);
|
|
4198
|
+
}
|
|
4134
4199
|
return lines.join("\n");
|
|
4135
4200
|
}
|
|
4136
4201
|
var getFlowInputSchema = z.object({
|
|
@@ -4659,37 +4724,7 @@ function validateActions(actions, result) {
|
|
|
4659
4724
|
for (const [name, action] of Object.entries(actions)) {
|
|
4660
4725
|
validateAction(action, `actions.${name}`, actionNames, result);
|
|
4661
4726
|
}
|
|
4662
|
-
|
|
4663
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
4664
|
-
const detectCycle = (actionName, stack = []) => {
|
|
4665
|
-
if (visiting.has(actionName)) {
|
|
4666
|
-
return [...stack, actionName];
|
|
4667
|
-
}
|
|
4668
|
-
if (visited.has(actionName)) {
|
|
4669
|
-
return null;
|
|
4670
|
-
}
|
|
4671
|
-
visiting.add(actionName);
|
|
4672
|
-
stack.push(actionName);
|
|
4673
|
-
const action = actions[actionName];
|
|
4674
|
-
if (action?.runAfter) {
|
|
4675
|
-
for (const depName of Object.keys(action.runAfter)) {
|
|
4676
|
-
if (actionNames.has(depName)) {
|
|
4677
|
-
const cycle = detectCycle(depName, stack);
|
|
4678
|
-
if (cycle) return cycle;
|
|
4679
|
-
}
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
visiting.delete(actionName);
|
|
4683
|
-
visited.add(actionName);
|
|
4684
|
-
return null;
|
|
4685
|
-
};
|
|
4686
|
-
for (const actionName of actionNames) {
|
|
4687
|
-
const cycle = detectCycle(actionName);
|
|
4688
|
-
if (cycle) {
|
|
4689
|
-
addError(result, "actions", `Circular dependency detected: ${cycle.join(" -> ")}`);
|
|
4690
|
-
break;
|
|
4691
|
-
}
|
|
4692
|
-
}
|
|
4727
|
+
detectCircularDependencies(actions, actionNames, "actions", result);
|
|
4693
4728
|
}
|
|
4694
4729
|
function validateAction(action, path, allActions, result) {
|
|
4695
4730
|
if (!action.type) {
|
|
@@ -4761,36 +4796,70 @@ function validateConnectorAction(action, path, result) {
|
|
|
4761
4796
|
addError(result, `${path}.inputs.host.operationId`, "Missing operation ID");
|
|
4762
4797
|
}
|
|
4763
4798
|
}
|
|
4764
|
-
function
|
|
4799
|
+
function validateNestedActions(actions, path, result) {
|
|
4800
|
+
const localScope = new Set(Object.keys(actions));
|
|
4801
|
+
for (const [name, subAction] of Object.entries(actions)) {
|
|
4802
|
+
validateAction(subAction, `${path}.${name}`, localScope, result);
|
|
4803
|
+
}
|
|
4804
|
+
detectCircularDependencies(actions, localScope, path, result);
|
|
4805
|
+
}
|
|
4806
|
+
function detectCircularDependencies(actions, actionNames, path, result) {
|
|
4807
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4808
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
4809
|
+
const detectCycle = (actionName, stack = []) => {
|
|
4810
|
+
if (visiting.has(actionName)) {
|
|
4811
|
+
return [...stack, actionName];
|
|
4812
|
+
}
|
|
4813
|
+
if (visited.has(actionName)) {
|
|
4814
|
+
return null;
|
|
4815
|
+
}
|
|
4816
|
+
visiting.add(actionName);
|
|
4817
|
+
stack.push(actionName);
|
|
4818
|
+
const action = actions[actionName];
|
|
4819
|
+
if (action?.runAfter) {
|
|
4820
|
+
for (const depName of Object.keys(action.runAfter)) {
|
|
4821
|
+
if (actionNames.has(depName)) {
|
|
4822
|
+
const cycle = detectCycle(depName, stack);
|
|
4823
|
+
if (cycle) return cycle;
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
}
|
|
4827
|
+
visiting.delete(actionName);
|
|
4828
|
+
visited.add(actionName);
|
|
4829
|
+
return null;
|
|
4830
|
+
};
|
|
4831
|
+
for (const actionName of actionNames) {
|
|
4832
|
+
const cycle = detectCycle(actionName);
|
|
4833
|
+
if (cycle) {
|
|
4834
|
+
addError(result, path, `Circular dependency detected: ${cycle.join(" -> ")}`);
|
|
4835
|
+
break;
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
}
|
|
4839
|
+
function validateConditionAction(action, path, _allActions, result) {
|
|
4765
4840
|
if (!action.expression) {
|
|
4766
4841
|
addError(result, `${path}.expression`, "Condition action must have an expression");
|
|
4767
4842
|
}
|
|
4768
4843
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4769
4844
|
addWarning(result, `${path}.actions`, "Condition 'if true' branch has no actions");
|
|
4770
4845
|
} else {
|
|
4771
|
-
|
|
4772
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4773
|
-
}
|
|
4846
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4774
4847
|
}
|
|
4775
4848
|
if (action.else?.actions) {
|
|
4776
|
-
|
|
4777
|
-
validateAction(subAction, `${path}.else.actions.${name}`, allActions, result);
|
|
4778
|
-
}
|
|
4849
|
+
validateNestedActions(action.else.actions, `${path}.else.actions`, result);
|
|
4779
4850
|
}
|
|
4780
4851
|
}
|
|
4781
|
-
function validateForeachAction(action, path,
|
|
4852
|
+
function validateForeachAction(action, path, _allActions, result) {
|
|
4782
4853
|
if (!action.foreach) {
|
|
4783
4854
|
addError(result, `${path}.foreach`, "Foreach action must have a foreach expression");
|
|
4784
4855
|
}
|
|
4785
4856
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4786
4857
|
addWarning(result, `${path}.actions`, "Foreach loop has no actions");
|
|
4787
4858
|
} else {
|
|
4788
|
-
|
|
4789
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4790
|
-
}
|
|
4859
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4791
4860
|
}
|
|
4792
4861
|
}
|
|
4793
|
-
function validateSwitchAction(action, path,
|
|
4862
|
+
function validateSwitchAction(action, path, _allActions, result) {
|
|
4794
4863
|
if (!action.expression) {
|
|
4795
4864
|
addError(result, `${path}.expression`, "Switch action must have an expression");
|
|
4796
4865
|
}
|
|
@@ -4799,37 +4868,29 @@ function validateSwitchAction(action, path, allActions, result) {
|
|
|
4799
4868
|
} else {
|
|
4800
4869
|
for (const [caseName, caseData] of Object.entries(action.cases)) {
|
|
4801
4870
|
if (caseData.actions) {
|
|
4802
|
-
|
|
4803
|
-
validateAction(subAction, `${path}.cases.${caseName}.actions.${name}`, allActions, result);
|
|
4804
|
-
}
|
|
4871
|
+
validateNestedActions(caseData.actions, `${path}.cases.${caseName}.actions`, result);
|
|
4805
4872
|
}
|
|
4806
4873
|
}
|
|
4807
4874
|
}
|
|
4808
4875
|
if (action.default?.actions) {
|
|
4809
|
-
|
|
4810
|
-
validateAction(subAction, `${path}.default.actions.${name}`, allActions, result);
|
|
4811
|
-
}
|
|
4876
|
+
validateNestedActions(action.default.actions, `${path}.default.actions`, result);
|
|
4812
4877
|
}
|
|
4813
4878
|
}
|
|
4814
|
-
function validateScopeAction(action, path,
|
|
4879
|
+
function validateScopeAction(action, path, _allActions, result) {
|
|
4815
4880
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4816
4881
|
addWarning(result, `${path}.actions`, "Scope has no actions");
|
|
4817
4882
|
} else {
|
|
4818
|
-
|
|
4819
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4820
|
-
}
|
|
4883
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4821
4884
|
}
|
|
4822
4885
|
}
|
|
4823
|
-
function validateUntilAction(action, path,
|
|
4886
|
+
function validateUntilAction(action, path, _allActions, result) {
|
|
4824
4887
|
if (!action.expression) {
|
|
4825
4888
|
addError(result, `${path}.expression`, "Until action must have an expression");
|
|
4826
4889
|
}
|
|
4827
4890
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4828
4891
|
addWarning(result, `${path}.actions`, "Until loop has no actions");
|
|
4829
4892
|
} else {
|
|
4830
|
-
|
|
4831
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4832
|
-
}
|
|
4893
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4833
4894
|
}
|
|
4834
4895
|
}
|
|
4835
4896
|
|
|
@@ -4962,14 +5023,56 @@ var KNOWN_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
4962
5023
|
"uriQuery",
|
|
4963
5024
|
"uriScheme"
|
|
4964
5025
|
]);
|
|
4965
|
-
|
|
4966
|
-
function extractExpressions(text) {
|
|
5026
|
+
function extractWrappedExpressions(text) {
|
|
4967
5027
|
const expressions = [];
|
|
5028
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
5029
|
+
if (text[i] !== "@") continue;
|
|
5030
|
+
if (text[i + 1] !== "{") continue;
|
|
5031
|
+
if (i > 0 && text[i - 1] === "@") continue;
|
|
5032
|
+
let depth = 0;
|
|
5033
|
+
let inString = false;
|
|
5034
|
+
let stringChar = "";
|
|
5035
|
+
let end = -1;
|
|
5036
|
+
for (let j = i + 2; j < text.length; j++) {
|
|
5037
|
+
const char = text[j];
|
|
5038
|
+
const prevChar = j > i + 2 ? text[j - 1] : "";
|
|
5039
|
+
if ((char === "'" || char === '"') && prevChar !== "\\") {
|
|
5040
|
+
if (!inString) {
|
|
5041
|
+
inString = true;
|
|
5042
|
+
stringChar = char;
|
|
5043
|
+
} else if (char === stringChar) {
|
|
5044
|
+
inString = false;
|
|
5045
|
+
}
|
|
5046
|
+
continue;
|
|
5047
|
+
}
|
|
5048
|
+
if (inString) continue;
|
|
5049
|
+
if (char === "{") {
|
|
5050
|
+
depth++;
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
if (char === "}") {
|
|
5054
|
+
if (depth === 0) {
|
|
5055
|
+
end = j;
|
|
5056
|
+
break;
|
|
5057
|
+
}
|
|
5058
|
+
depth--;
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5061
|
+
if (end !== -1) {
|
|
5062
|
+
expressions.push(text.slice(i + 2, end));
|
|
5063
|
+
i = end;
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
return expressions;
|
|
5067
|
+
}
|
|
5068
|
+
var BARE_EXPRESSION_REGEX = /@((?:triggerBody|triggerOutputs|outputs|body|items|variables|parameters|workflow|result|actionOutputs|actionBody|trigger|actions|concat|toLower|toUpper|trim|substring|replace|split|length|contains|startsWith|endsWith|indexOf|lastIndexOf|first|last|take|skip|join|createArray|union|intersection|empty|if|and|or|not|equals|greater|greaterOrEquals|less|lessOrEquals|coalesce|int|float|string|bool|json|xml|array|add|sub|mul|div|mod|max|min|utcNow|addDays|addHours|addMinutes|addSeconds|formatDateTime|guid|base64|encodeUriComponent|decodeUriComponent)\s*\(.*\))/g;
|
|
5069
|
+
function extractExpressions(text) {
|
|
5070
|
+
const expressions = extractWrappedExpressions(text);
|
|
4968
5071
|
let match;
|
|
4969
|
-
while ((match =
|
|
5072
|
+
while ((match = BARE_EXPRESSION_REGEX.exec(text)) !== null) {
|
|
4970
5073
|
if (match[1]) expressions.push(match[1]);
|
|
4971
5074
|
}
|
|
4972
|
-
|
|
5075
|
+
BARE_EXPRESSION_REGEX.lastIndex = 0;
|
|
4973
5076
|
return expressions;
|
|
4974
5077
|
}
|
|
4975
5078
|
function validateExpression(expression, path) {
|
|
@@ -5255,7 +5358,7 @@ function checkBestPractices(definition) {
|
|
|
5255
5358
|
const type = actionObj.type;
|
|
5256
5359
|
if (type === "Http" || type === "ApiConnection") {
|
|
5257
5360
|
const inputs = actionObj.inputs;
|
|
5258
|
-
const retryPolicy = actionObj.retryPolicy;
|
|
5361
|
+
const retryPolicy = inputs?.retryPolicy ?? actionObj.retryPolicy;
|
|
5259
5362
|
if (!retryPolicy || retryPolicy.type === "none") {
|
|
5260
5363
|
issues.push({
|
|
5261
5364
|
severity: "suggestion",
|
|
@@ -5441,6 +5544,160 @@ function getAllValidationWarnings(result) {
|
|
|
5441
5544
|
return warnings;
|
|
5442
5545
|
}
|
|
5443
5546
|
|
|
5547
|
+
// src/utils/expression-escape.ts
|
|
5548
|
+
var logger3 = createChildLogger({ module: "expression-escape" });
|
|
5549
|
+
function containsExpressions(value) {
|
|
5550
|
+
if (typeof value === "string") {
|
|
5551
|
+
return value.includes("@{") || value.startsWith("@") && !value.startsWith("@@");
|
|
5552
|
+
}
|
|
5553
|
+
if (Array.isArray(value)) {
|
|
5554
|
+
return value.some(containsExpressions);
|
|
5555
|
+
}
|
|
5556
|
+
if (value && typeof value === "object") {
|
|
5557
|
+
return Object.values(value).some(containsExpressions);
|
|
5558
|
+
}
|
|
5559
|
+
return false;
|
|
5560
|
+
}
|
|
5561
|
+
function objectToStringExpression(obj) {
|
|
5562
|
+
const jsonStr = JSON.stringify(obj);
|
|
5563
|
+
const segments = [];
|
|
5564
|
+
let pos = 0;
|
|
5565
|
+
while (pos < jsonStr.length) {
|
|
5566
|
+
const exprStart = jsonStr.indexOf("@{", pos);
|
|
5567
|
+
if (exprStart === -1) {
|
|
5568
|
+
segments.push("'" + escapeForConcat(jsonStr.slice(pos)) + "'");
|
|
5569
|
+
break;
|
|
5570
|
+
}
|
|
5571
|
+
const isJsonStringValue = exprStart > 0 && jsonStr[exprStart - 1] === '"';
|
|
5572
|
+
if (exprStart > pos) {
|
|
5573
|
+
let literal = jsonStr.slice(pos, exprStart);
|
|
5574
|
+
if (isJsonStringValue) {
|
|
5575
|
+
literal = literal.slice(0, -1);
|
|
5576
|
+
}
|
|
5577
|
+
if (literal.length > 0) {
|
|
5578
|
+
segments.push("'" + escapeForConcat(literal) + "'");
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
let depth = 0;
|
|
5582
|
+
let exprEnd = -1;
|
|
5583
|
+
for (let i = exprStart + 2; i < jsonStr.length; i++) {
|
|
5584
|
+
if (jsonStr[i] === "{") depth++;
|
|
5585
|
+
else if (jsonStr[i] === "}") {
|
|
5586
|
+
if (depth === 0) {
|
|
5587
|
+
exprEnd = i;
|
|
5588
|
+
break;
|
|
5589
|
+
}
|
|
5590
|
+
depth--;
|
|
5591
|
+
}
|
|
5592
|
+
}
|
|
5593
|
+
if (exprEnd === -1) {
|
|
5594
|
+
segments.push("'" + escapeForConcat(jsonStr.slice(exprStart)) + "'");
|
|
5595
|
+
break;
|
|
5596
|
+
}
|
|
5597
|
+
const expr = jsonStr.slice(exprStart + 2, exprEnd);
|
|
5598
|
+
if (isJsonStringValue) {
|
|
5599
|
+
segments.push(`'"'`);
|
|
5600
|
+
segments.push(expr);
|
|
5601
|
+
segments.push(`'"'`);
|
|
5602
|
+
} else {
|
|
5603
|
+
segments.push(expr);
|
|
5604
|
+
}
|
|
5605
|
+
pos = exprEnd + 1;
|
|
5606
|
+
if (isJsonStringValue && pos < jsonStr.length && jsonStr[pos] === '"') {
|
|
5607
|
+
pos++;
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
const filtered = segments.filter((s) => s !== "''" && s !== "");
|
|
5611
|
+
if (filtered.length === 0) return jsonStr;
|
|
5612
|
+
if (filtered.length === 1 && !filtered[0].startsWith("'")) {
|
|
5613
|
+
return `@{${filtered[0]}}`;
|
|
5614
|
+
}
|
|
5615
|
+
return `@{json(concat(${filtered.join(",")}))}`;
|
|
5616
|
+
}
|
|
5617
|
+
function escapeForConcat(s) {
|
|
5618
|
+
return s.replace(/'/g, "''");
|
|
5619
|
+
}
|
|
5620
|
+
function preprocessActionInputs(inputs, actionName) {
|
|
5621
|
+
const processedInputs = { ...inputs };
|
|
5622
|
+
if (processedInputs.body && typeof processedInputs.body === "object") {
|
|
5623
|
+
const bodyStr = JSON.stringify(processedInputs.body);
|
|
5624
|
+
const hasAtKeys = /"@[a-zA-Z]/.test(bodyStr);
|
|
5625
|
+
if (hasAtKeys || containsExpressions(processedInputs.body)) {
|
|
5626
|
+
logger3.info({ action: actionName, field: "body", hasAtKeys }, "Converting action payload object to string expression");
|
|
5627
|
+
processedInputs.body = objectToStringExpression(processedInputs.body);
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
if (processedInputs.parameters && typeof processedInputs.parameters === "object" && !Array.isArray(processedInputs.parameters)) {
|
|
5631
|
+
const parameters = processedInputs.parameters;
|
|
5632
|
+
let changed = false;
|
|
5633
|
+
const processedParameters = { ...parameters };
|
|
5634
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
5635
|
+
if (value && typeof value === "object" && containsExpressions(value)) {
|
|
5636
|
+
logger3.info({ action: actionName, field: `parameters.${key}` }, "Converting connector parameter object to string expression");
|
|
5637
|
+
processedParameters[key] = objectToStringExpression(value);
|
|
5638
|
+
changed = true;
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
if (changed) {
|
|
5642
|
+
processedInputs.parameters = processedParameters;
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5645
|
+
return processedInputs;
|
|
5646
|
+
}
|
|
5647
|
+
function preprocessFlowActions(actions) {
|
|
5648
|
+
const processed = {};
|
|
5649
|
+
for (const [name, action] of Object.entries(actions)) {
|
|
5650
|
+
if (!action || typeof action !== "object") {
|
|
5651
|
+
processed[name] = action;
|
|
5652
|
+
continue;
|
|
5653
|
+
}
|
|
5654
|
+
const actionObj = action;
|
|
5655
|
+
processed[name] = { ...actionObj };
|
|
5656
|
+
const processedAction = processed[name];
|
|
5657
|
+
if (actionObj.inputs && typeof actionObj.inputs === "object") {
|
|
5658
|
+
processedAction.inputs = preprocessActionInputs(
|
|
5659
|
+
actionObj.inputs,
|
|
5660
|
+
name
|
|
5661
|
+
);
|
|
5662
|
+
}
|
|
5663
|
+
if (actionObj.actions && typeof actionObj.actions === "object") {
|
|
5664
|
+
processedAction.actions = preprocessFlowActions(
|
|
5665
|
+
actionObj.actions
|
|
5666
|
+
);
|
|
5667
|
+
}
|
|
5668
|
+
if (actionObj.else && typeof actionObj.else === "object") {
|
|
5669
|
+
const elseBranch = actionObj.else;
|
|
5670
|
+
if (elseBranch.actions && typeof elseBranch.actions === "object") {
|
|
5671
|
+
processedAction.else = {
|
|
5672
|
+
...elseBranch,
|
|
5673
|
+
actions: preprocessFlowActions(elseBranch.actions)
|
|
5674
|
+
};
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
if (actionObj.cases && typeof actionObj.cases === "object") {
|
|
5678
|
+
const cases = actionObj.cases;
|
|
5679
|
+
const processedCases = {};
|
|
5680
|
+
for (const [caseName, caseVal] of Object.entries(cases)) {
|
|
5681
|
+
if (caseVal && typeof caseVal === "object") {
|
|
5682
|
+
const cv = caseVal;
|
|
5683
|
+
if (cv.actions && typeof cv.actions === "object") {
|
|
5684
|
+
processedCases[caseName] = {
|
|
5685
|
+
...cv,
|
|
5686
|
+
actions: preprocessFlowActions(cv.actions)
|
|
5687
|
+
};
|
|
5688
|
+
} else {
|
|
5689
|
+
processedCases[caseName] = caseVal;
|
|
5690
|
+
}
|
|
5691
|
+
} else {
|
|
5692
|
+
processedCases[caseName] = caseVal;
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
processedAction.cases = processedCases;
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
return processed;
|
|
5699
|
+
}
|
|
5700
|
+
|
|
5444
5701
|
// src/tools/create-flow.ts
|
|
5445
5702
|
var triggerSchema = z.object({
|
|
5446
5703
|
type: z.string().describe("Trigger type (e.g., 'Request', 'Recurrence', 'OpenApiConnection')"),
|
|
@@ -5452,12 +5709,12 @@ var triggerSchema = z.object({
|
|
|
5452
5709
|
startTime: z.string().optional(),
|
|
5453
5710
|
timeZone: z.string().optional()
|
|
5454
5711
|
}).optional().describe("Recurrence settings for scheduled triggers")
|
|
5455
|
-
});
|
|
5712
|
+
}).passthrough();
|
|
5456
5713
|
var actionSchema = z.object({
|
|
5457
5714
|
type: z.string().describe("Action type"),
|
|
5458
5715
|
inputs: z.unknown().optional().describe("Action inputs - can be object, string, array, or any value"),
|
|
5459
5716
|
runAfter: z.record(z.array(z.enum(["Succeeded", "Failed", "Skipped", "TimedOut"]))).optional().describe("Dependencies")
|
|
5460
|
-
});
|
|
5717
|
+
}).passthrough();
|
|
5461
5718
|
var connectionRefSchema = z.object({
|
|
5462
5719
|
connectionName: z.string().describe("Connection name in the environment"),
|
|
5463
5720
|
id: z.string().describe("Connection resource ID"),
|
|
@@ -5549,13 +5806,14 @@ var createFlowTool = {
|
|
|
5549
5806
|
async function handleCreateFlow(api, input) {
|
|
5550
5807
|
const parsed = createFlowInputSchema.parse(input);
|
|
5551
5808
|
logger.info({ displayName: parsed.displayName }, "Executing create_flow");
|
|
5809
|
+
const processedActions = preprocessFlowActions(parsed.actions);
|
|
5552
5810
|
const definition = {
|
|
5553
5811
|
$schema: "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
|
|
5554
5812
|
contentVersion: "1.0.0.0",
|
|
5555
5813
|
triggers: {
|
|
5556
5814
|
[parsed.triggerName ?? "manual"]: parsed.trigger
|
|
5557
5815
|
},
|
|
5558
|
-
actions:
|
|
5816
|
+
actions: processedActions
|
|
5559
5817
|
};
|
|
5560
5818
|
if (parsed.connectionReferences && Object.keys(parsed.connectionReferences).length > 0) {
|
|
5561
5819
|
definition.parameters = {
|
|
@@ -5656,12 +5914,12 @@ var triggerSchema2 = z.object({
|
|
|
5656
5914
|
startTime: z.string().optional(),
|
|
5657
5915
|
timeZone: z.string().optional()
|
|
5658
5916
|
}).optional()
|
|
5659
|
-
});
|
|
5917
|
+
}).passthrough();
|
|
5660
5918
|
var actionSchema2 = z.object({
|
|
5661
5919
|
type: z.string(),
|
|
5662
5920
|
inputs: z.unknown().optional(),
|
|
5663
5921
|
runAfter: z.record(z.array(z.enum(["Succeeded", "Failed", "Skipped", "TimedOut"]))).optional()
|
|
5664
|
-
});
|
|
5922
|
+
}).passthrough();
|
|
5665
5923
|
var connectionRefSchema2 = z.object({
|
|
5666
5924
|
connectionName: z.string(),
|
|
5667
5925
|
id: z.string(),
|
|
@@ -5730,11 +5988,12 @@ async function handleUpdateFlow(api, input) {
|
|
|
5730
5988
|
let newDefinition;
|
|
5731
5989
|
if (parsed.trigger || parsed.actions) {
|
|
5732
5990
|
const triggerName = parsed.triggerName ?? Object.keys(currentDef.triggers)[0] ?? "manual";
|
|
5991
|
+
const processedActions = parsed.actions ? preprocessFlowActions(parsed.actions) : currentDef.actions;
|
|
5733
5992
|
newDefinition = {
|
|
5734
5993
|
$schema: currentDef.$schema,
|
|
5735
5994
|
contentVersion: currentDef.contentVersion,
|
|
5736
5995
|
triggers: parsed.trigger ? { [triggerName]: parsed.trigger } : currentDef.triggers,
|
|
5737
|
-
actions:
|
|
5996
|
+
actions: processedActions
|
|
5738
5997
|
};
|
|
5739
5998
|
if (currentDef.parameters) {
|
|
5740
5999
|
newDefinition.parameters = currentDef.parameters;
|
|
@@ -13525,7 +13784,8 @@ function formatEnvironmentDetails(env2) {
|
|
|
13525
13784
|
};
|
|
13526
13785
|
}
|
|
13527
13786
|
async function getEnvironmentSummary(context, environmentId) {
|
|
13528
|
-
const
|
|
13787
|
+
const result = await context.api.listFlows({ environment: environmentId });
|
|
13788
|
+
const flows = result.flows;
|
|
13529
13789
|
const flowStats = {
|
|
13530
13790
|
total: flows.length,
|
|
13531
13791
|
started: flows.filter((f) => f.properties.state === "Started").length,
|
|
@@ -14097,6 +14357,122 @@ function getPrompt(name, args) {
|
|
|
14097
14357
|
return null;
|
|
14098
14358
|
}
|
|
14099
14359
|
}
|
|
14360
|
+
var PACKAGE_NAME = "powerautomate-mcp";
|
|
14361
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
14362
|
+
var CHECK_TIMEOUT_MS = 5e3;
|
|
14363
|
+
function getInstalledVersion() {
|
|
14364
|
+
try {
|
|
14365
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
14366
|
+
for (let i = 0; i < 5; i++) {
|
|
14367
|
+
const candidate = join(dir, "package.json");
|
|
14368
|
+
if (existsSync(candidate)) {
|
|
14369
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
14370
|
+
if (pkg.name === PACKAGE_NAME && pkg.version) {
|
|
14371
|
+
return pkg.version;
|
|
14372
|
+
}
|
|
14373
|
+
}
|
|
14374
|
+
const parent = dirname(dir);
|
|
14375
|
+
if (parent === dir) break;
|
|
14376
|
+
dir = parent;
|
|
14377
|
+
}
|
|
14378
|
+
return "0.0.0";
|
|
14379
|
+
} catch {
|
|
14380
|
+
return "0.0.0";
|
|
14381
|
+
}
|
|
14382
|
+
}
|
|
14383
|
+
async function checkForUpdate() {
|
|
14384
|
+
const currentVersion = getInstalledVersion();
|
|
14385
|
+
try {
|
|
14386
|
+
const { statusCode, body } = await request(REGISTRY_URL, {
|
|
14387
|
+
method: "GET",
|
|
14388
|
+
headers: { accept: "application/json" },
|
|
14389
|
+
signal: AbortSignal.timeout(CHECK_TIMEOUT_MS)
|
|
14390
|
+
});
|
|
14391
|
+
if (statusCode !== 200) {
|
|
14392
|
+
await body.text();
|
|
14393
|
+
return null;
|
|
14394
|
+
}
|
|
14395
|
+
const data = await body.json();
|
|
14396
|
+
const latestVersion = data.version;
|
|
14397
|
+
if (!latestVersion) return null;
|
|
14398
|
+
return {
|
|
14399
|
+
currentVersion,
|
|
14400
|
+
latestVersion,
|
|
14401
|
+
updateAvailable: compareVersions(latestVersion, currentVersion) > 0
|
|
14402
|
+
};
|
|
14403
|
+
} catch (err) {
|
|
14404
|
+
logger.debug({ err }, "Update check failed (non-fatal)");
|
|
14405
|
+
return null;
|
|
14406
|
+
}
|
|
14407
|
+
}
|
|
14408
|
+
function compareVersions(a, b) {
|
|
14409
|
+
const pa = a.split(".").map(Number);
|
|
14410
|
+
const pb = b.split(".").map(Number);
|
|
14411
|
+
for (let i = 0; i < 3; i++) {
|
|
14412
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
14413
|
+
if (diff !== 0) return diff;
|
|
14414
|
+
}
|
|
14415
|
+
return 0;
|
|
14416
|
+
}
|
|
14417
|
+
function getUpdateCommand() {
|
|
14418
|
+
const npmExecPath = process.env["npm_execpath"] ?? "";
|
|
14419
|
+
const npmCommand = process.env["npm_command"] ?? "";
|
|
14420
|
+
if (npmExecPath.includes("npx") || npmCommand === "exec") {
|
|
14421
|
+
return { cmd: "npx", args: [`${PACKAGE_NAME}@latest`] };
|
|
14422
|
+
}
|
|
14423
|
+
if (npmExecPath.includes("yarn")) {
|
|
14424
|
+
return { cmd: "yarn", args: ["global", "add", PACKAGE_NAME] };
|
|
14425
|
+
}
|
|
14426
|
+
if (npmExecPath.includes("pnpm")) {
|
|
14427
|
+
return { cmd: "pnpm", args: ["add", "-g", PACKAGE_NAME] };
|
|
14428
|
+
}
|
|
14429
|
+
return { cmd: "npm", args: ["install", "-g", PACKAGE_NAME] };
|
|
14430
|
+
}
|
|
14431
|
+
async function runSelfUpdate() {
|
|
14432
|
+
const info = await checkForUpdate();
|
|
14433
|
+
if (info === null) {
|
|
14434
|
+
console.error("Could not reach npm registry. Check your internet connection.");
|
|
14435
|
+
process.exit(1);
|
|
14436
|
+
}
|
|
14437
|
+
if (!info.updateAvailable) {
|
|
14438
|
+
console.error(`Already up to date (v${info.currentVersion}).`);
|
|
14439
|
+
process.exit(0);
|
|
14440
|
+
}
|
|
14441
|
+
console.error(`Updating ${PACKAGE_NAME}: v${info.currentVersion} \u2192 v${info.latestVersion}`);
|
|
14442
|
+
const { cmd, args } = getUpdateCommand();
|
|
14443
|
+
console.error(`Running: ${cmd} ${args.join(" ")}`);
|
|
14444
|
+
return new Promise((_resolve) => {
|
|
14445
|
+
const child = execFile(cmd, args, { timeout: 12e4 }, (error, stdout, stderr) => {
|
|
14446
|
+
if (error) {
|
|
14447
|
+
console.error(`
|
|
14448
|
+
Update failed: ${error.message}`);
|
|
14449
|
+
if (stderr) console.error(stderr);
|
|
14450
|
+
console.error(`
|
|
14451
|
+
You can update manually: npm install -g ${PACKAGE_NAME}`);
|
|
14452
|
+
process.exit(1);
|
|
14453
|
+
}
|
|
14454
|
+
if (stdout) console.error(stdout.trim());
|
|
14455
|
+
console.error(`
|
|
14456
|
+
Updated to v${info.latestVersion} successfully.`);
|
|
14457
|
+
process.exit(0);
|
|
14458
|
+
});
|
|
14459
|
+
child.stdout?.pipe(process.stderr);
|
|
14460
|
+
child.stderr?.pipe(process.stderr);
|
|
14461
|
+
});
|
|
14462
|
+
}
|
|
14463
|
+
function backgroundUpdateCheck() {
|
|
14464
|
+
checkForUpdate().then((info) => {
|
|
14465
|
+
if (info?.updateAvailable) {
|
|
14466
|
+
console.error(
|
|
14467
|
+
`
|
|
14468
|
+
Update available: v${info.currentVersion} \u2192 v${info.latestVersion}
|
|
14469
|
+
Run: powerautomate-mcp --update
|
|
14470
|
+
`
|
|
14471
|
+
);
|
|
14472
|
+
}
|
|
14473
|
+
}).catch(() => {
|
|
14474
|
+
});
|
|
14475
|
+
}
|
|
14100
14476
|
|
|
14101
14477
|
// src/server.ts
|
|
14102
14478
|
async function createMcpServer(serverConfig) {
|
|
@@ -14176,7 +14552,7 @@ async function createMcpServer(serverConfig) {
|
|
|
14176
14552
|
const server = new Server(
|
|
14177
14553
|
{
|
|
14178
14554
|
name: "powerautomate-mcp",
|
|
14179
|
-
version:
|
|
14555
|
+
version: getInstalledVersion()
|
|
14180
14556
|
},
|
|
14181
14557
|
{
|
|
14182
14558
|
capabilities: {
|
|
@@ -14190,8 +14566,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14190
14566
|
logger.debug("Listing tools");
|
|
14191
14567
|
return { tools };
|
|
14192
14568
|
});
|
|
14193
|
-
server.setRequestHandler(CallToolRequestSchema, async (
|
|
14194
|
-
const { name, arguments: args } =
|
|
14569
|
+
server.setRequestHandler(CallToolRequestSchema, async (request8) => {
|
|
14570
|
+
const { name, arguments: args } = request8.params;
|
|
14195
14571
|
logger.info({ tool: name }, "Tool called");
|
|
14196
14572
|
if (args && !checkObjectDepth(args, 20)) {
|
|
14197
14573
|
return {
|
|
@@ -14219,8 +14595,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14219
14595
|
const resources = await listAllResources(resourceContext);
|
|
14220
14596
|
return { resources };
|
|
14221
14597
|
});
|
|
14222
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (
|
|
14223
|
-
const { uri } =
|
|
14598
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request8) => {
|
|
14599
|
+
const { uri } = request8.params;
|
|
14224
14600
|
logger.info({ uri }, "Reading resource");
|
|
14225
14601
|
const result = await readResource(resourceContext, uri);
|
|
14226
14602
|
if (!result) {
|
|
@@ -14232,8 +14608,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14232
14608
|
logger.debug("Listing prompts");
|
|
14233
14609
|
return { prompts };
|
|
14234
14610
|
});
|
|
14235
|
-
server.setRequestHandler(GetPromptRequestSchema, async (
|
|
14236
|
-
const { name, arguments: args } =
|
|
14611
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request8) => {
|
|
14612
|
+
const { name, arguments: args } = request8.params;
|
|
14237
14613
|
logger.info({ prompt: name }, "Getting prompt");
|
|
14238
14614
|
const result = getPrompt(name, args ?? {});
|
|
14239
14615
|
if (!result) {
|
|
@@ -14335,8 +14711,9 @@ function checkObjectDepth(obj, maxDepth, current = 0) {
|
|
|
14335
14711
|
return true;
|
|
14336
14712
|
}
|
|
14337
14713
|
async function createDeviceCodeAuthProvider(config, silentOnly = false) {
|
|
14338
|
-
const { clientId, cachePath } = config;
|
|
14339
|
-
const
|
|
14714
|
+
const { clientId, tenantId, cachePath } = config;
|
|
14715
|
+
const validatedTenantId = tenantId ? validateTenantId(tenantId) : null;
|
|
14716
|
+
const authority = validatedTenantId ? `https://login.microsoftonline.com/${validatedTenantId}` : DEFAULT_AUTHORITY;
|
|
14340
14717
|
const cachePlugin = await createPersistentCachePlugin(cachePath);
|
|
14341
14718
|
const msalConfig = {
|
|
14342
14719
|
auth: {
|
|
@@ -14481,7 +14858,9 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
|
|
|
14481
14858
|
}
|
|
14482
14859
|
async function createAuthProviderFromConfig2(clientId, tenantId, cachePath, silentOnly = false) {
|
|
14483
14860
|
const config = {
|
|
14484
|
-
clientId
|
|
14861
|
+
clientId,
|
|
14862
|
+
tenantId: tenantId ?? null
|
|
14863
|
+
};
|
|
14485
14864
|
return createDeviceCodeAuthProvider(config, silentOnly);
|
|
14486
14865
|
}
|
|
14487
14866
|
|
|
@@ -14737,19 +15116,25 @@ function clearTokenCache() {
|
|
|
14737
15116
|
return { cleared: files.length > 0, files };
|
|
14738
15117
|
}
|
|
14739
15118
|
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14740
|
-
function
|
|
15119
|
+
function readExistingAuthConfig() {
|
|
14741
15120
|
try {
|
|
14742
15121
|
const configPath = getConfigPath();
|
|
14743
|
-
if (!existsSync(configPath)) return null;
|
|
15122
|
+
if (!existsSync(configPath)) return { clientId: null, tenantId: null };
|
|
14744
15123
|
const raw = readFileSync(configPath, "utf-8");
|
|
14745
15124
|
const parsed = JSON.parse(raw);
|
|
14746
|
-
const
|
|
14747
|
-
|
|
14748
|
-
return
|
|
15125
|
+
const clientId = parsed?.auth?.clientId;
|
|
15126
|
+
const tenantId = parsed?.auth?.tenantId;
|
|
15127
|
+
return {
|
|
15128
|
+
clientId: clientId && UUID_RE2.test(clientId) ? clientId : null,
|
|
15129
|
+
tenantId: tenantId && UUID_RE2.test(tenantId) ? tenantId : null
|
|
15130
|
+
};
|
|
14749
15131
|
} catch {
|
|
14750
|
-
return null;
|
|
15132
|
+
return { clientId: null, tenantId: null };
|
|
14751
15133
|
}
|
|
14752
15134
|
}
|
|
15135
|
+
function readExistingClientId() {
|
|
15136
|
+
return readExistingAuthConfig().clientId;
|
|
15137
|
+
}
|
|
14753
15138
|
async function promptForClientId(askUser) {
|
|
14754
15139
|
print("");
|
|
14755
15140
|
print(` ${c.yellow}You need a Microsoft Entra app registration for your tenant.${c.reset}`);
|
|
@@ -14840,8 +15225,9 @@ async function runSetupWizard() {
|
|
|
14840
15225
|
print(` ${c.dim}Sign in with your Microsoft work account.${c.reset}`);
|
|
14841
15226
|
print(` ${c.dim}A device code will be shown \u2014 follow the instructions to authenticate.${c.reset}`);
|
|
14842
15227
|
print("");
|
|
14843
|
-
const
|
|
14844
|
-
let tenantId =
|
|
15228
|
+
const existingAuth = readExistingAuthConfig();
|
|
15229
|
+
let tenantId = existingAuth.tenantId;
|
|
15230
|
+
const authProvider = await createAuthProviderFromConfig2(clientId, tenantId);
|
|
14845
15231
|
try {
|
|
14846
15232
|
await authProvider.getToken();
|
|
14847
15233
|
const account = authProvider.getCurrentAccount();
|
|
@@ -14997,6 +15383,71 @@ function isSetupNeeded() {
|
|
|
14997
15383
|
return !existsSync(configPath);
|
|
14998
15384
|
}
|
|
14999
15385
|
|
|
15386
|
+
// src/cli/validate.ts
|
|
15387
|
+
async function runValidate() {
|
|
15388
|
+
console.error("Validating Power Automate MCP configuration...\n");
|
|
15389
|
+
let config;
|
|
15390
|
+
try {
|
|
15391
|
+
const configPath = getConfigPath();
|
|
15392
|
+
console.error(` [1/3] Config file: ${configPath}`);
|
|
15393
|
+
config = loadConfig();
|
|
15394
|
+
const envNames = Object.keys(config.environments).filter((k) => k !== "default");
|
|
15395
|
+
console.error(` Client ID: ${config.auth.clientId}`);
|
|
15396
|
+
console.error(` Tenant: ${config.auth.tenantId ?? "common"}`);
|
|
15397
|
+
console.error(` Environments: ${envNames.join(", ") || "(none)"}`);
|
|
15398
|
+
console.error(` Default: ${config.environments.default}`);
|
|
15399
|
+
console.error(" \u2713 Config valid\n");
|
|
15400
|
+
} catch (err) {
|
|
15401
|
+
const msg = err instanceof ConfigurationError ? err.message : String(err);
|
|
15402
|
+
console.error(` \u2717 Config error: ${msg}
|
|
15403
|
+
`);
|
|
15404
|
+
process.exit(1);
|
|
15405
|
+
}
|
|
15406
|
+
let authProvider;
|
|
15407
|
+
try {
|
|
15408
|
+
console.error(" [2/3] Authenticating...");
|
|
15409
|
+
authProvider = await createAuthProviderFromConfig(
|
|
15410
|
+
config.auth.clientId,
|
|
15411
|
+
config.auth.tenantId,
|
|
15412
|
+
void 0,
|
|
15413
|
+
true
|
|
15414
|
+
// silentOnly
|
|
15415
|
+
);
|
|
15416
|
+
const token = await authProvider.getToken();
|
|
15417
|
+
console.error(` Token acquired (${token.length} chars)`);
|
|
15418
|
+
const account = authProvider.getCurrentAccount();
|
|
15419
|
+
if (account) {
|
|
15420
|
+
console.error(` Account: ${account.username ?? account.name ?? "unknown"}`);
|
|
15421
|
+
}
|
|
15422
|
+
console.error(" \u2713 Auth valid\n");
|
|
15423
|
+
} catch (err) {
|
|
15424
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15425
|
+
console.error(` \u2717 Auth error: ${msg}`);
|
|
15426
|
+
console.error(" Run: powerautomate-mcp --setup\n");
|
|
15427
|
+
process.exit(1);
|
|
15428
|
+
}
|
|
15429
|
+
try {
|
|
15430
|
+
console.error(" [3/3] Testing API connectivity...");
|
|
15431
|
+
const defaultEnv = resolveEnvironment(config);
|
|
15432
|
+
const flowApi = new FlowManagementApi({
|
|
15433
|
+
authProvider,
|
|
15434
|
+
defaultEnvironmentId: defaultEnv.id
|
|
15435
|
+
});
|
|
15436
|
+
const result = await flowApi.listFlows({ environment: defaultEnv.id, top: 1 });
|
|
15437
|
+
const count = result.flows.length;
|
|
15438
|
+
console.error(` Environment: ${defaultEnv.name} (${defaultEnv.id})`);
|
|
15439
|
+
console.error(` Flow API reachable (${count >= 1 ? "flows found" : "connected"})`);
|
|
15440
|
+
console.error(" \u2713 Connectivity valid\n");
|
|
15441
|
+
} catch (err) {
|
|
15442
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15443
|
+
console.error(` \u2717 Connectivity error: ${msg}
|
|
15444
|
+
`);
|
|
15445
|
+
process.exit(1);
|
|
15446
|
+
}
|
|
15447
|
+
console.error("All checks passed. Server is ready to run.\n");
|
|
15448
|
+
process.exit(0);
|
|
15449
|
+
}
|
|
15450
|
+
|
|
15000
15451
|
// src/index.ts
|
|
15001
15452
|
function parseArgs() {
|
|
15002
15453
|
const args = process.argv.slice(2);
|
|
@@ -15008,11 +15459,27 @@ function parseArgs() {
|
|
|
15008
15459
|
port = parsed;
|
|
15009
15460
|
}
|
|
15010
15461
|
}
|
|
15462
|
+
let env2;
|
|
15463
|
+
const envIdx = args.indexOf("--env");
|
|
15464
|
+
if (envIdx !== -1 && args[envIdx + 1] && !args[envIdx + 1].startsWith("-")) {
|
|
15465
|
+
env2 = args[envIdx + 1];
|
|
15466
|
+
}
|
|
15467
|
+
let config;
|
|
15468
|
+
const configIdx = args.indexOf("--config");
|
|
15469
|
+
if (configIdx !== -1 && args[configIdx + 1] && !args[configIdx + 1].startsWith("-")) {
|
|
15470
|
+
config = args[configIdx + 1];
|
|
15471
|
+
}
|
|
15011
15472
|
return {
|
|
15012
15473
|
setup: args.includes("--setup") || args.includes("-s"),
|
|
15013
15474
|
help: args.includes("--help") || args.includes("-h"),
|
|
15475
|
+
version: args.includes("--version") || args.includes("-v"),
|
|
15476
|
+
validate: args.includes("--validate"),
|
|
15477
|
+
update: args.includes("--update"),
|
|
15014
15478
|
http: args.includes("--http"),
|
|
15015
|
-
port
|
|
15479
|
+
port,
|
|
15480
|
+
env: env2,
|
|
15481
|
+
config,
|
|
15482
|
+
debug: args.includes("--debug")
|
|
15016
15483
|
};
|
|
15017
15484
|
}
|
|
15018
15485
|
function getConfigPathHint() {
|
|
@@ -15026,16 +15493,23 @@ function getConfigPathHint() {
|
|
|
15026
15493
|
}
|
|
15027
15494
|
function printHelp() {
|
|
15028
15495
|
const configPath = getConfigPathHint();
|
|
15496
|
+
const version = getInstalledVersion();
|
|
15029
15497
|
console.log(`
|
|
15030
|
-
Power Automate MCP Server
|
|
15498
|
+
Power Automate MCP Server v${version}
|
|
15031
15499
|
|
|
15032
15500
|
Usage: powerautomate-mcp [options]
|
|
15033
15501
|
|
|
15034
15502
|
Options:
|
|
15035
|
-
--setup, -s
|
|
15036
|
-
--
|
|
15037
|
-
--
|
|
15038
|
-
--
|
|
15503
|
+
--setup, -s Run the interactive setup wizard
|
|
15504
|
+
--validate Verify config, auth, and API connectivity then exit
|
|
15505
|
+
--update Check for updates and install the latest version
|
|
15506
|
+
--version, -v Print version and exit
|
|
15507
|
+
--http Start with Streamable HTTP transport (for ChatGPT / remote clients)
|
|
15508
|
+
--port <N> Port for HTTP transport (default: 3000)
|
|
15509
|
+
--env <name> Override the default environment (alias or GUID)
|
|
15510
|
+
--config <path> Use an alternate config file
|
|
15511
|
+
--debug Enable debug-level logging
|
|
15512
|
+
--help, -h Show this help message
|
|
15039
15513
|
|
|
15040
15514
|
Environment Variables:
|
|
15041
15515
|
PA_MCP_CLIENT_ID Microsoft Entra app client ID (overrides config file)
|
|
@@ -15056,10 +15530,30 @@ For more information, see the README.md file.
|
|
|
15056
15530
|
}
|
|
15057
15531
|
async function main() {
|
|
15058
15532
|
const args = parseArgs();
|
|
15533
|
+
if (args.version) {
|
|
15534
|
+
console.log(getInstalledVersion());
|
|
15535
|
+
process.exit(0);
|
|
15536
|
+
}
|
|
15059
15537
|
if (args.help) {
|
|
15060
15538
|
printHelp();
|
|
15061
15539
|
process.exit(0);
|
|
15062
15540
|
}
|
|
15541
|
+
if (args.update) {
|
|
15542
|
+
await runSelfUpdate();
|
|
15543
|
+
return;
|
|
15544
|
+
}
|
|
15545
|
+
if (args.debug) {
|
|
15546
|
+
logger.level = "debug";
|
|
15547
|
+
logger.debug("Debug logging enabled via --debug");
|
|
15548
|
+
}
|
|
15549
|
+
if (args.config) {
|
|
15550
|
+
process.env["PA_CONFIG_PATH"] = args.config;
|
|
15551
|
+
logger.debug({ configPath: args.config }, "Using custom config path");
|
|
15552
|
+
}
|
|
15553
|
+
if (args.validate) {
|
|
15554
|
+
await runValidate();
|
|
15555
|
+
return;
|
|
15556
|
+
}
|
|
15063
15557
|
if (args.setup || isSetupNeeded()) {
|
|
15064
15558
|
const result = await runSetupWizard();
|
|
15065
15559
|
if (!result.success) {
|
|
@@ -15072,7 +15566,11 @@ Setup failed: ${result.error}`);
|
|
|
15072
15566
|
process.exit(0);
|
|
15073
15567
|
}
|
|
15074
15568
|
}
|
|
15075
|
-
logger.info(
|
|
15569
|
+
logger.info(
|
|
15570
|
+
{ platform: process.platform, nodeVersion: process.version, version: getInstalledVersion() },
|
|
15571
|
+
"Starting Power Automate MCP"
|
|
15572
|
+
);
|
|
15573
|
+
backgroundUpdateCheck();
|
|
15076
15574
|
let config;
|
|
15077
15575
|
try {
|
|
15078
15576
|
config = loadConfig();
|
|
@@ -15089,6 +15587,10 @@ Configuration Error: ${error.message}
|
|
|
15089
15587
|
}
|
|
15090
15588
|
throw error;
|
|
15091
15589
|
}
|
|
15590
|
+
if (args.env) {
|
|
15591
|
+
config.environments.default = args.env;
|
|
15592
|
+
logger.info({ env: args.env }, "Default environment overridden via --env");
|
|
15593
|
+
}
|
|
15092
15594
|
try {
|
|
15093
15595
|
const { start } = await createMcpServer({
|
|
15094
15596
|
config,
|