powerautomate-mcp 0.5.3 → 0.6.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 +457 -185
- 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,20 +381,7 @@ Create the file with your Microsoft Entra app client ID and Power Automate envir
|
|
|
381
381
|
err
|
|
382
382
|
);
|
|
383
383
|
}
|
|
384
|
-
|
|
385
|
-
if (envClientId) {
|
|
386
|
-
const UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
387
|
-
if (!UUID_RE3.test(envClientId)) {
|
|
388
|
-
throw new ConfigurationError(
|
|
389
|
-
`PA_MCP_CLIENT_ID is not a valid UUID: ${envClientId.substring(0, 50)}`
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
parsed.auth = {
|
|
393
|
-
...parsed.auth ?? {},
|
|
394
|
-
clientId: envClientId
|
|
395
|
-
};
|
|
396
|
-
logger.info("Using client ID from PA_MCP_CLIENT_ID environment variable");
|
|
397
|
-
}
|
|
384
|
+
parsed = interpolateEnvVars(parsed);
|
|
398
385
|
try {
|
|
399
386
|
const config = validateConfig(parsed);
|
|
400
387
|
logger.info(
|
|
@@ -423,12 +410,34 @@ function resolveEnvironment(config, envNameOrId) {
|
|
|
423
410
|
displayName: envConfig.displayName
|
|
424
411
|
};
|
|
425
412
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
413
|
+
{
|
|
414
|
+
throw new ConfigurationError(
|
|
415
|
+
`Environment "${envKey}" not found in configuration. Available environments: ${Object.keys(config.environments).filter((k) => k !== "default").join(", ") || "(none)"}`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function interpolateEnvVars(value) {
|
|
420
|
+
if (typeof value === "string") {
|
|
421
|
+
return value.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
422
|
+
const envValue = env[varName];
|
|
423
|
+
if (envValue === void 0) {
|
|
424
|
+
logger.warn({ varName }, "Environment variable referenced in config is not set");
|
|
425
|
+
return "";
|
|
426
|
+
}
|
|
427
|
+
return envValue;
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (Array.isArray(value)) {
|
|
431
|
+
return value.map(interpolateEnvVars);
|
|
432
|
+
}
|
|
433
|
+
if (value !== null && typeof value === "object") {
|
|
434
|
+
const result = {};
|
|
435
|
+
for (const [key, val] of Object.entries(value)) {
|
|
436
|
+
result[key] = interpolateEnvVars(val);
|
|
437
|
+
}
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
return value;
|
|
432
441
|
}
|
|
433
442
|
|
|
434
443
|
// src/utils/security.ts
|
|
@@ -1126,12 +1135,12 @@ var FlowManagementApi = class {
|
|
|
1126
1135
|
throw new ApiError(`${context}: API error`, statusCode, errorDetails);
|
|
1127
1136
|
}
|
|
1128
1137
|
if (!responseText) {
|
|
1129
|
-
return {};
|
|
1138
|
+
return { value: [] };
|
|
1130
1139
|
}
|
|
1131
1140
|
return JSON.parse(responseText);
|
|
1132
1141
|
}
|
|
1133
1142
|
/**
|
|
1134
|
-
* List flows in an environment
|
|
1143
|
+
* List flows in an environment with optional pagination support
|
|
1135
1144
|
*/
|
|
1136
1145
|
async listFlows(options = {}) {
|
|
1137
1146
|
const envId = options.environment ?? this.defaultEnvironmentId;
|
|
@@ -1154,8 +1163,8 @@ var FlowManagementApi = class {
|
|
|
1154
1163
|
response,
|
|
1155
1164
|
"List flows"
|
|
1156
1165
|
);
|
|
1157
|
-
apiLogger.info({ count: data.value.length }, "Listed flows");
|
|
1158
|
-
return data.value;
|
|
1166
|
+
apiLogger.info({ count: data.value.length, hasMore: !!data.nextLink }, "Listed flows");
|
|
1167
|
+
return { flows: data.value, nextLink: data.nextLink };
|
|
1159
1168
|
}, "List flows");
|
|
1160
1169
|
}
|
|
1161
1170
|
/**
|
|
@@ -1448,9 +1457,13 @@ var FlowManagementApi = class {
|
|
|
1448
1457
|
const safeFlowId = validateRowId(flowId);
|
|
1449
1458
|
const safeRunId = validateRowId(runId);
|
|
1450
1459
|
apiLogger.info({ flowId: safeFlowId, runId: safeRunId, envId }, "Resubmitting flow run");
|
|
1460
|
+
const flow = await this.getFlow(safeFlowId, envId);
|
|
1461
|
+
const triggers = flow.properties.definition?.triggers ?? {};
|
|
1462
|
+
const triggerName = Object.keys(triggers)[0] ?? "manual";
|
|
1463
|
+
apiLogger.debug({ flowId: safeFlowId, triggerName }, "Using trigger name for resubmit");
|
|
1451
1464
|
const url = this.buildUrl(
|
|
1452
1465
|
envId,
|
|
1453
|
-
`/flows/${safeFlowId}/triggers/
|
|
1466
|
+
`/flows/${safeFlowId}/triggers/${triggerName}/histories/${safeRunId}/resubmit`
|
|
1454
1467
|
);
|
|
1455
1468
|
const headers = await this.getAuthHeaders();
|
|
1456
1469
|
return wrapApiCall(async () => {
|
|
@@ -1549,7 +1562,7 @@ var FlowManagementApi = class {
|
|
|
1549
1562
|
*/
|
|
1550
1563
|
async listApprovals(environmentId, top) {
|
|
1551
1564
|
const envId = environmentId ?? this.defaultEnvironmentId;
|
|
1552
|
-
const limit = top ?? 25;
|
|
1565
|
+
const limit = Math.min(top ?? 25, MAX_PAGE_SIZE);
|
|
1553
1566
|
apiLogger.debug({ envId, limit }, "Listing approvals");
|
|
1554
1567
|
const url = this.buildUrl(envId, "/approvals", { $top: limit });
|
|
1555
1568
|
const headers = await this.getAuthHeaders();
|
|
@@ -1631,6 +1644,12 @@ var ConnectorMetadataApi = class {
|
|
|
1631
1644
|
*/
|
|
1632
1645
|
async handleResponse(response, context) {
|
|
1633
1646
|
const { statusCode, body } = response;
|
|
1647
|
+
if (statusCode === 429) {
|
|
1648
|
+
await body.text();
|
|
1649
|
+
const retryAfter = response.headers["retry-after"];
|
|
1650
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
1651
|
+
throw new RateLimitError(retryMs);
|
|
1652
|
+
}
|
|
1634
1653
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
1635
1654
|
const chunks = [];
|
|
1636
1655
|
let totalSize = 0;
|
|
@@ -1965,6 +1984,12 @@ var GraphApi = class {
|
|
|
1965
1984
|
*/
|
|
1966
1985
|
async handleBinaryResponse(response, context) {
|
|
1967
1986
|
const { statusCode, body, headers } = response;
|
|
1987
|
+
if (statusCode === 429) {
|
|
1988
|
+
await body.text();
|
|
1989
|
+
const retryAfter = response.headers["retry-after"];
|
|
1990
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
1991
|
+
throw new RateLimitError(retryMs);
|
|
1992
|
+
}
|
|
1968
1993
|
if (statusCode >= 400) {
|
|
1969
1994
|
const responseText = await body.text();
|
|
1970
1995
|
graphLogger.error({ statusCode, context }, "Graph API binary response error");
|
|
@@ -1993,6 +2018,12 @@ var GraphApi = class {
|
|
|
1993
2018
|
}
|
|
1994
2019
|
async handleResponse(response, context) {
|
|
1995
2020
|
const { statusCode, body } = response;
|
|
2021
|
+
if (statusCode === 429) {
|
|
2022
|
+
await body.text();
|
|
2023
|
+
const retryAfter = response.headers["retry-after"];
|
|
2024
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
2025
|
+
throw new RateLimitError(retryMs);
|
|
2026
|
+
}
|
|
1996
2027
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
1997
2028
|
const chunks = [];
|
|
1998
2029
|
let totalSize = 0;
|
|
@@ -2015,7 +2046,7 @@ var GraphApi = class {
|
|
|
2015
2046
|
throw new ApiError(`${context}: Graph API error`, statusCode, errorDetails);
|
|
2016
2047
|
}
|
|
2017
2048
|
if (!responseText) {
|
|
2018
|
-
return {};
|
|
2049
|
+
return { value: [] };
|
|
2019
2050
|
}
|
|
2020
2051
|
return JSON.parse(responseText);
|
|
2021
2052
|
}
|
|
@@ -3262,6 +3293,12 @@ var DataverseApi = class {
|
|
|
3262
3293
|
*/
|
|
3263
3294
|
async handleResponse(response, context) {
|
|
3264
3295
|
const { statusCode, body } = response;
|
|
3296
|
+
if (statusCode === 429) {
|
|
3297
|
+
await body.text();
|
|
3298
|
+
const retryAfter = response.headers["retry-after"];
|
|
3299
|
+
const retryMs = retryAfter ? parseInt(String(retryAfter), 10) * 1e3 : void 0;
|
|
3300
|
+
throw new RateLimitError(retryMs);
|
|
3301
|
+
}
|
|
3265
3302
|
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024;
|
|
3266
3303
|
const chunks = [];
|
|
3267
3304
|
let totalSize = 0;
|
|
@@ -3287,7 +3324,7 @@ var DataverseApi = class {
|
|
|
3287
3324
|
throw new ApiError(`${context}: API error`, statusCode, errorDetails);
|
|
3288
3325
|
}
|
|
3289
3326
|
if (!responseText) {
|
|
3290
|
-
return {};
|
|
3327
|
+
return { value: [] };
|
|
3291
3328
|
}
|
|
3292
3329
|
return JSON.parse(responseText);
|
|
3293
3330
|
}
|
|
@@ -4080,6 +4117,7 @@ var SchemaCache = class {
|
|
|
4080
4117
|
};
|
|
4081
4118
|
var listFlowsInputSchema = z.object({
|
|
4082
4119
|
environment: z.string().optional().describe("Environment name or GUID. Uses default if omitted."),
|
|
4120
|
+
search: z.string().optional().describe("Search flows by display name (case-insensitive substring match). Applied client-side after fetching."),
|
|
4083
4121
|
filter: z.string().optional().describe(`OData filter expression (e.g., "properties/state eq 'Started'")`),
|
|
4084
4122
|
top: z.number().min(1).max(250).optional().describe("Maximum number of results to return (default 50, max 250)")
|
|
4085
4123
|
});
|
|
@@ -4093,6 +4131,10 @@ var listFlowsTool = {
|
|
|
4093
4131
|
type: "string",
|
|
4094
4132
|
description: "Environment name or GUID. Uses default if omitted."
|
|
4095
4133
|
},
|
|
4134
|
+
search: {
|
|
4135
|
+
type: "string",
|
|
4136
|
+
description: "Search flows by display name (case-insensitive substring match)."
|
|
4137
|
+
},
|
|
4096
4138
|
filter: {
|
|
4097
4139
|
type: "string",
|
|
4098
4140
|
description: `OData filter expression (e.g., "properties/state eq 'Started'")`
|
|
@@ -4117,11 +4159,16 @@ async function handleListFlows(api, input) {
|
|
|
4117
4159
|
return `Invalid filter: ${err instanceof Error ? err.message : "Filter contains disallowed patterns"}`;
|
|
4118
4160
|
}
|
|
4119
4161
|
}
|
|
4120
|
-
const
|
|
4162
|
+
const result = await api.listFlows({
|
|
4121
4163
|
environment: parsed.environment,
|
|
4122
4164
|
filter: safeFilter,
|
|
4123
4165
|
top: parsed.top ?? 50
|
|
4124
4166
|
});
|
|
4167
|
+
let flows = result.flows;
|
|
4168
|
+
if (parsed.search) {
|
|
4169
|
+
const needle = parsed.search.toLowerCase();
|
|
4170
|
+
flows = flows.filter((f) => f.properties.displayName.toLowerCase().includes(needle));
|
|
4171
|
+
}
|
|
4125
4172
|
if (flows.length === 0) {
|
|
4126
4173
|
return "No flows found";
|
|
4127
4174
|
}
|
|
@@ -4131,6 +4178,10 @@ async function handleListFlows(api, input) {
|
|
|
4131
4178
|
const modified = new Date(flow.properties.lastModifiedTime).toLocaleDateString();
|
|
4132
4179
|
lines.push(` ${state} ${flow.properties.displayName} (${flow.name}) - ${modified}`);
|
|
4133
4180
|
}
|
|
4181
|
+
if (result.nextLink) {
|
|
4182
|
+
lines.push(`
|
|
4183
|
+
(More flows available \u2014 use skipToken for next page)`);
|
|
4184
|
+
}
|
|
4134
4185
|
return lines.join("\n");
|
|
4135
4186
|
}
|
|
4136
4187
|
var getFlowInputSchema = z.object({
|
|
@@ -4659,37 +4710,7 @@ function validateActions(actions, result) {
|
|
|
4659
4710
|
for (const [name, action] of Object.entries(actions)) {
|
|
4660
4711
|
validateAction(action, `actions.${name}`, actionNames, result);
|
|
4661
4712
|
}
|
|
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
|
-
}
|
|
4713
|
+
detectCircularDependencies(actions, actionNames, "actions", result);
|
|
4693
4714
|
}
|
|
4694
4715
|
function validateAction(action, path, allActions, result) {
|
|
4695
4716
|
if (!action.type) {
|
|
@@ -4761,36 +4782,70 @@ function validateConnectorAction(action, path, result) {
|
|
|
4761
4782
|
addError(result, `${path}.inputs.host.operationId`, "Missing operation ID");
|
|
4762
4783
|
}
|
|
4763
4784
|
}
|
|
4764
|
-
function
|
|
4785
|
+
function validateNestedActions(actions, path, result) {
|
|
4786
|
+
const localScope = new Set(Object.keys(actions));
|
|
4787
|
+
for (const [name, subAction] of Object.entries(actions)) {
|
|
4788
|
+
validateAction(subAction, `${path}.${name}`, localScope, result);
|
|
4789
|
+
}
|
|
4790
|
+
detectCircularDependencies(actions, localScope, path, result);
|
|
4791
|
+
}
|
|
4792
|
+
function detectCircularDependencies(actions, actionNames, path, result) {
|
|
4793
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4794
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
4795
|
+
const detectCycle = (actionName, stack = []) => {
|
|
4796
|
+
if (visiting.has(actionName)) {
|
|
4797
|
+
return [...stack, actionName];
|
|
4798
|
+
}
|
|
4799
|
+
if (visited.has(actionName)) {
|
|
4800
|
+
return null;
|
|
4801
|
+
}
|
|
4802
|
+
visiting.add(actionName);
|
|
4803
|
+
stack.push(actionName);
|
|
4804
|
+
const action = actions[actionName];
|
|
4805
|
+
if (action?.runAfter) {
|
|
4806
|
+
for (const depName of Object.keys(action.runAfter)) {
|
|
4807
|
+
if (actionNames.has(depName)) {
|
|
4808
|
+
const cycle = detectCycle(depName, stack);
|
|
4809
|
+
if (cycle) return cycle;
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
visiting.delete(actionName);
|
|
4814
|
+
visited.add(actionName);
|
|
4815
|
+
return null;
|
|
4816
|
+
};
|
|
4817
|
+
for (const actionName of actionNames) {
|
|
4818
|
+
const cycle = detectCycle(actionName);
|
|
4819
|
+
if (cycle) {
|
|
4820
|
+
addError(result, path, `Circular dependency detected: ${cycle.join(" -> ")}`);
|
|
4821
|
+
break;
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
function validateConditionAction(action, path, _allActions, result) {
|
|
4765
4826
|
if (!action.expression) {
|
|
4766
4827
|
addError(result, `${path}.expression`, "Condition action must have an expression");
|
|
4767
4828
|
}
|
|
4768
4829
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4769
4830
|
addWarning(result, `${path}.actions`, "Condition 'if true' branch has no actions");
|
|
4770
4831
|
} else {
|
|
4771
|
-
|
|
4772
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4773
|
-
}
|
|
4832
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4774
4833
|
}
|
|
4775
4834
|
if (action.else?.actions) {
|
|
4776
|
-
|
|
4777
|
-
validateAction(subAction, `${path}.else.actions.${name}`, allActions, result);
|
|
4778
|
-
}
|
|
4835
|
+
validateNestedActions(action.else.actions, `${path}.else.actions`, result);
|
|
4779
4836
|
}
|
|
4780
4837
|
}
|
|
4781
|
-
function validateForeachAction(action, path,
|
|
4838
|
+
function validateForeachAction(action, path, _allActions, result) {
|
|
4782
4839
|
if (!action.foreach) {
|
|
4783
4840
|
addError(result, `${path}.foreach`, "Foreach action must have a foreach expression");
|
|
4784
4841
|
}
|
|
4785
4842
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4786
4843
|
addWarning(result, `${path}.actions`, "Foreach loop has no actions");
|
|
4787
4844
|
} else {
|
|
4788
|
-
|
|
4789
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4790
|
-
}
|
|
4845
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4791
4846
|
}
|
|
4792
4847
|
}
|
|
4793
|
-
function validateSwitchAction(action, path,
|
|
4848
|
+
function validateSwitchAction(action, path, _allActions, result) {
|
|
4794
4849
|
if (!action.expression) {
|
|
4795
4850
|
addError(result, `${path}.expression`, "Switch action must have an expression");
|
|
4796
4851
|
}
|
|
@@ -4799,37 +4854,29 @@ function validateSwitchAction(action, path, allActions, result) {
|
|
|
4799
4854
|
} else {
|
|
4800
4855
|
for (const [caseName, caseData] of Object.entries(action.cases)) {
|
|
4801
4856
|
if (caseData.actions) {
|
|
4802
|
-
|
|
4803
|
-
validateAction(subAction, `${path}.cases.${caseName}.actions.${name}`, allActions, result);
|
|
4804
|
-
}
|
|
4857
|
+
validateNestedActions(caseData.actions, `${path}.cases.${caseName}.actions`, result);
|
|
4805
4858
|
}
|
|
4806
4859
|
}
|
|
4807
4860
|
}
|
|
4808
4861
|
if (action.default?.actions) {
|
|
4809
|
-
|
|
4810
|
-
validateAction(subAction, `${path}.default.actions.${name}`, allActions, result);
|
|
4811
|
-
}
|
|
4862
|
+
validateNestedActions(action.default.actions, `${path}.default.actions`, result);
|
|
4812
4863
|
}
|
|
4813
4864
|
}
|
|
4814
|
-
function validateScopeAction(action, path,
|
|
4865
|
+
function validateScopeAction(action, path, _allActions, result) {
|
|
4815
4866
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4816
4867
|
addWarning(result, `${path}.actions`, "Scope has no actions");
|
|
4817
4868
|
} else {
|
|
4818
|
-
|
|
4819
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4820
|
-
}
|
|
4869
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4821
4870
|
}
|
|
4822
4871
|
}
|
|
4823
|
-
function validateUntilAction(action, path,
|
|
4872
|
+
function validateUntilAction(action, path, _allActions, result) {
|
|
4824
4873
|
if (!action.expression) {
|
|
4825
4874
|
addError(result, `${path}.expression`, "Until action must have an expression");
|
|
4826
4875
|
}
|
|
4827
4876
|
if (!action.actions || Object.keys(action.actions).length === 0) {
|
|
4828
4877
|
addWarning(result, `${path}.actions`, "Until loop has no actions");
|
|
4829
4878
|
} else {
|
|
4830
|
-
|
|
4831
|
-
validateAction(subAction, `${path}.actions.${name}`, allActions, result);
|
|
4832
|
-
}
|
|
4879
|
+
validateNestedActions(action.actions, `${path}.actions`, result);
|
|
4833
4880
|
}
|
|
4834
4881
|
}
|
|
4835
4882
|
|
|
@@ -4963,6 +5010,7 @@ var KNOWN_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
4963
5010
|
"uriScheme"
|
|
4964
5011
|
]);
|
|
4965
5012
|
var EXPRESSION_REGEX = /@\{([^}]+)\}/g;
|
|
5013
|
+
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;
|
|
4966
5014
|
function extractExpressions(text) {
|
|
4967
5015
|
const expressions = [];
|
|
4968
5016
|
let match;
|
|
@@ -4970,6 +5018,10 @@ function extractExpressions(text) {
|
|
|
4970
5018
|
if (match[1]) expressions.push(match[1]);
|
|
4971
5019
|
}
|
|
4972
5020
|
EXPRESSION_REGEX.lastIndex = 0;
|
|
5021
|
+
while ((match = BARE_EXPRESSION_REGEX.exec(text)) !== null) {
|
|
5022
|
+
if (match[1]) expressions.push(match[1]);
|
|
5023
|
+
}
|
|
5024
|
+
BARE_EXPRESSION_REGEX.lastIndex = 0;
|
|
4973
5025
|
return expressions;
|
|
4974
5026
|
}
|
|
4975
5027
|
function validateExpression(expression, path) {
|
|
@@ -5255,7 +5307,7 @@ function checkBestPractices(definition) {
|
|
|
5255
5307
|
const type = actionObj.type;
|
|
5256
5308
|
if (type === "Http" || type === "ApiConnection") {
|
|
5257
5309
|
const inputs = actionObj.inputs;
|
|
5258
|
-
const retryPolicy = actionObj.retryPolicy;
|
|
5310
|
+
const retryPolicy = inputs?.retryPolicy ?? actionObj.retryPolicy;
|
|
5259
5311
|
if (!retryPolicy || retryPolicy.type === "none") {
|
|
5260
5312
|
issues.push({
|
|
5261
5313
|
severity: "suggestion",
|
|
@@ -5452,12 +5504,12 @@ var triggerSchema = z.object({
|
|
|
5452
5504
|
startTime: z.string().optional(),
|
|
5453
5505
|
timeZone: z.string().optional()
|
|
5454
5506
|
}).optional().describe("Recurrence settings for scheduled triggers")
|
|
5455
|
-
});
|
|
5507
|
+
}).passthrough();
|
|
5456
5508
|
var actionSchema = z.object({
|
|
5457
5509
|
type: z.string().describe("Action type"),
|
|
5458
5510
|
inputs: z.unknown().optional().describe("Action inputs - can be object, string, array, or any value"),
|
|
5459
5511
|
runAfter: z.record(z.array(z.enum(["Succeeded", "Failed", "Skipped", "TimedOut"]))).optional().describe("Dependencies")
|
|
5460
|
-
});
|
|
5512
|
+
}).passthrough();
|
|
5461
5513
|
var connectionRefSchema = z.object({
|
|
5462
5514
|
connectionName: z.string().describe("Connection name in the environment"),
|
|
5463
5515
|
id: z.string().describe("Connection resource ID"),
|
|
@@ -5656,12 +5708,12 @@ var triggerSchema2 = z.object({
|
|
|
5656
5708
|
startTime: z.string().optional(),
|
|
5657
5709
|
timeZone: z.string().optional()
|
|
5658
5710
|
}).optional()
|
|
5659
|
-
});
|
|
5711
|
+
}).passthrough();
|
|
5660
5712
|
var actionSchema2 = z.object({
|
|
5661
5713
|
type: z.string(),
|
|
5662
5714
|
inputs: z.unknown().optional(),
|
|
5663
5715
|
runAfter: z.record(z.array(z.enum(["Succeeded", "Failed", "Skipped", "TimedOut"]))).optional()
|
|
5664
|
-
});
|
|
5716
|
+
}).passthrough();
|
|
5665
5717
|
var connectionRefSchema2 = z.object({
|
|
5666
5718
|
connectionName: z.string(),
|
|
5667
5719
|
id: z.string(),
|
|
@@ -13525,7 +13577,8 @@ function formatEnvironmentDetails(env2) {
|
|
|
13525
13577
|
};
|
|
13526
13578
|
}
|
|
13527
13579
|
async function getEnvironmentSummary(context, environmentId) {
|
|
13528
|
-
const
|
|
13580
|
+
const result = await context.api.listFlows({ environment: environmentId });
|
|
13581
|
+
const flows = result.flows;
|
|
13529
13582
|
const flowStats = {
|
|
13530
13583
|
total: flows.length,
|
|
13531
13584
|
started: flows.filter((f) => f.properties.state === "Started").length,
|
|
@@ -14097,6 +14150,122 @@ function getPrompt(name, args) {
|
|
|
14097
14150
|
return null;
|
|
14098
14151
|
}
|
|
14099
14152
|
}
|
|
14153
|
+
var PACKAGE_NAME = "powerautomate-mcp";
|
|
14154
|
+
var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
14155
|
+
var CHECK_TIMEOUT_MS = 5e3;
|
|
14156
|
+
function getInstalledVersion() {
|
|
14157
|
+
try {
|
|
14158
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
14159
|
+
for (let i = 0; i < 5; i++) {
|
|
14160
|
+
const candidate = join(dir, "package.json");
|
|
14161
|
+
if (existsSync(candidate)) {
|
|
14162
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf-8"));
|
|
14163
|
+
if (pkg.name === PACKAGE_NAME && pkg.version) {
|
|
14164
|
+
return pkg.version;
|
|
14165
|
+
}
|
|
14166
|
+
}
|
|
14167
|
+
const parent = dirname(dir);
|
|
14168
|
+
if (parent === dir) break;
|
|
14169
|
+
dir = parent;
|
|
14170
|
+
}
|
|
14171
|
+
return "0.0.0";
|
|
14172
|
+
} catch {
|
|
14173
|
+
return "0.0.0";
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
async function checkForUpdate() {
|
|
14177
|
+
const currentVersion = getInstalledVersion();
|
|
14178
|
+
try {
|
|
14179
|
+
const { statusCode, body } = await request(REGISTRY_URL, {
|
|
14180
|
+
method: "GET",
|
|
14181
|
+
headers: { accept: "application/json" },
|
|
14182
|
+
signal: AbortSignal.timeout(CHECK_TIMEOUT_MS)
|
|
14183
|
+
});
|
|
14184
|
+
if (statusCode !== 200) {
|
|
14185
|
+
await body.text();
|
|
14186
|
+
return null;
|
|
14187
|
+
}
|
|
14188
|
+
const data = await body.json();
|
|
14189
|
+
const latestVersion = data.version;
|
|
14190
|
+
if (!latestVersion) return null;
|
|
14191
|
+
return {
|
|
14192
|
+
currentVersion,
|
|
14193
|
+
latestVersion,
|
|
14194
|
+
updateAvailable: compareVersions(latestVersion, currentVersion) > 0
|
|
14195
|
+
};
|
|
14196
|
+
} catch (err) {
|
|
14197
|
+
logger.debug({ err }, "Update check failed (non-fatal)");
|
|
14198
|
+
return null;
|
|
14199
|
+
}
|
|
14200
|
+
}
|
|
14201
|
+
function compareVersions(a, b) {
|
|
14202
|
+
const pa = a.split(".").map(Number);
|
|
14203
|
+
const pb = b.split(".").map(Number);
|
|
14204
|
+
for (let i = 0; i < 3; i++) {
|
|
14205
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
14206
|
+
if (diff !== 0) return diff;
|
|
14207
|
+
}
|
|
14208
|
+
return 0;
|
|
14209
|
+
}
|
|
14210
|
+
function getUpdateCommand() {
|
|
14211
|
+
const npmExecPath = process.env["npm_execpath"] ?? "";
|
|
14212
|
+
const npmCommand = process.env["npm_command"] ?? "";
|
|
14213
|
+
if (npmExecPath.includes("npx") || npmCommand === "exec") {
|
|
14214
|
+
return { cmd: "npx", args: [`${PACKAGE_NAME}@latest`] };
|
|
14215
|
+
}
|
|
14216
|
+
if (npmExecPath.includes("yarn")) {
|
|
14217
|
+
return { cmd: "yarn", args: ["global", "add", PACKAGE_NAME] };
|
|
14218
|
+
}
|
|
14219
|
+
if (npmExecPath.includes("pnpm")) {
|
|
14220
|
+
return { cmd: "pnpm", args: ["add", "-g", PACKAGE_NAME] };
|
|
14221
|
+
}
|
|
14222
|
+
return { cmd: "npm", args: ["install", "-g", PACKAGE_NAME] };
|
|
14223
|
+
}
|
|
14224
|
+
async function runSelfUpdate() {
|
|
14225
|
+
const info = await checkForUpdate();
|
|
14226
|
+
if (info === null) {
|
|
14227
|
+
console.error("Could not reach npm registry. Check your internet connection.");
|
|
14228
|
+
process.exit(1);
|
|
14229
|
+
}
|
|
14230
|
+
if (!info.updateAvailable) {
|
|
14231
|
+
console.error(`Already up to date (v${info.currentVersion}).`);
|
|
14232
|
+
process.exit(0);
|
|
14233
|
+
}
|
|
14234
|
+
console.error(`Updating ${PACKAGE_NAME}: v${info.currentVersion} \u2192 v${info.latestVersion}`);
|
|
14235
|
+
const { cmd, args } = getUpdateCommand();
|
|
14236
|
+
console.error(`Running: ${cmd} ${args.join(" ")}`);
|
|
14237
|
+
return new Promise((_resolve) => {
|
|
14238
|
+
const child = execFile(cmd, args, { timeout: 12e4 }, (error, stdout, stderr) => {
|
|
14239
|
+
if (error) {
|
|
14240
|
+
console.error(`
|
|
14241
|
+
Update failed: ${error.message}`);
|
|
14242
|
+
if (stderr) console.error(stderr);
|
|
14243
|
+
console.error(`
|
|
14244
|
+
You can update manually: npm install -g ${PACKAGE_NAME}`);
|
|
14245
|
+
process.exit(1);
|
|
14246
|
+
}
|
|
14247
|
+
if (stdout) console.error(stdout.trim());
|
|
14248
|
+
console.error(`
|
|
14249
|
+
Updated to v${info.latestVersion} successfully.`);
|
|
14250
|
+
process.exit(0);
|
|
14251
|
+
});
|
|
14252
|
+
child.stdout?.pipe(process.stderr);
|
|
14253
|
+
child.stderr?.pipe(process.stderr);
|
|
14254
|
+
});
|
|
14255
|
+
}
|
|
14256
|
+
function backgroundUpdateCheck() {
|
|
14257
|
+
checkForUpdate().then((info) => {
|
|
14258
|
+
if (info?.updateAvailable) {
|
|
14259
|
+
console.error(
|
|
14260
|
+
`
|
|
14261
|
+
Update available: v${info.currentVersion} \u2192 v${info.latestVersion}
|
|
14262
|
+
Run: powerautomate-mcp --update
|
|
14263
|
+
`
|
|
14264
|
+
);
|
|
14265
|
+
}
|
|
14266
|
+
}).catch(() => {
|
|
14267
|
+
});
|
|
14268
|
+
}
|
|
14100
14269
|
|
|
14101
14270
|
// src/server.ts
|
|
14102
14271
|
async function createMcpServer(serverConfig) {
|
|
@@ -14176,7 +14345,7 @@ async function createMcpServer(serverConfig) {
|
|
|
14176
14345
|
const server = new Server(
|
|
14177
14346
|
{
|
|
14178
14347
|
name: "powerautomate-mcp",
|
|
14179
|
-
version:
|
|
14348
|
+
version: getInstalledVersion()
|
|
14180
14349
|
},
|
|
14181
14350
|
{
|
|
14182
14351
|
capabilities: {
|
|
@@ -14190,8 +14359,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14190
14359
|
logger.debug("Listing tools");
|
|
14191
14360
|
return { tools };
|
|
14192
14361
|
});
|
|
14193
|
-
server.setRequestHandler(CallToolRequestSchema, async (
|
|
14194
|
-
const { name, arguments: args } =
|
|
14362
|
+
server.setRequestHandler(CallToolRequestSchema, async (request8) => {
|
|
14363
|
+
const { name, arguments: args } = request8.params;
|
|
14195
14364
|
logger.info({ tool: name }, "Tool called");
|
|
14196
14365
|
if (args && !checkObjectDepth(args, 20)) {
|
|
14197
14366
|
return {
|
|
@@ -14219,8 +14388,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14219
14388
|
const resources = await listAllResources(resourceContext);
|
|
14220
14389
|
return { resources };
|
|
14221
14390
|
});
|
|
14222
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (
|
|
14223
|
-
const { uri } =
|
|
14391
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request8) => {
|
|
14392
|
+
const { uri } = request8.params;
|
|
14224
14393
|
logger.info({ uri }, "Reading resource");
|
|
14225
14394
|
const result = await readResource(resourceContext, uri);
|
|
14226
14395
|
if (!result) {
|
|
@@ -14232,8 +14401,8 @@ async function createMcpServer(serverConfig) {
|
|
|
14232
14401
|
logger.debug("Listing prompts");
|
|
14233
14402
|
return { prompts };
|
|
14234
14403
|
});
|
|
14235
|
-
server.setRequestHandler(GetPromptRequestSchema, async (
|
|
14236
|
-
const { name, arguments: args } =
|
|
14404
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request8) => {
|
|
14405
|
+
const { name, arguments: args } = request8.params;
|
|
14237
14406
|
logger.info({ prompt: name }, "Getting prompt");
|
|
14238
14407
|
const result = getPrompt(name, args ?? {});
|
|
14239
14408
|
if (!result) {
|
|
@@ -14335,8 +14504,9 @@ function checkObjectDepth(obj, maxDepth, current = 0) {
|
|
|
14335
14504
|
return true;
|
|
14336
14505
|
}
|
|
14337
14506
|
async function createDeviceCodeAuthProvider(config, silentOnly = false) {
|
|
14338
|
-
const { clientId, cachePath } = config;
|
|
14339
|
-
const
|
|
14507
|
+
const { clientId, tenantId, cachePath } = config;
|
|
14508
|
+
const validatedTenantId = tenantId ? validateTenantId(tenantId) : null;
|
|
14509
|
+
const authority = validatedTenantId ? `https://login.microsoftonline.com/${validatedTenantId}` : DEFAULT_AUTHORITY;
|
|
14340
14510
|
const cachePlugin = await createPersistentCachePlugin(cachePath);
|
|
14341
14511
|
const msalConfig = {
|
|
14342
14512
|
auth: {
|
|
@@ -14481,7 +14651,9 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
|
|
|
14481
14651
|
}
|
|
14482
14652
|
async function createAuthProviderFromConfig2(clientId, tenantId, cachePath, silentOnly = false) {
|
|
14483
14653
|
const config = {
|
|
14484
|
-
clientId
|
|
14654
|
+
clientId,
|
|
14655
|
+
tenantId: tenantId ?? null
|
|
14656
|
+
};
|
|
14485
14657
|
return createDeviceCodeAuthProvider(config, silentOnly);
|
|
14486
14658
|
}
|
|
14487
14659
|
|
|
@@ -14533,6 +14705,7 @@ function getDefaultEnvironment(environments) {
|
|
|
14533
14705
|
}
|
|
14534
14706
|
|
|
14535
14707
|
// src/setup/published-app.ts
|
|
14708
|
+
var PUBLISHED_APP_CLIENT_ID = "20ae33ee-6743-4786-9b00-8eb7bfebfd11";
|
|
14536
14709
|
var APP_DISPLAY_NAME = "Power Automate MCP";
|
|
14537
14710
|
var REDIRECT_URI = "https://login.microsoftonline.com/common/oauth2/nativeclient";
|
|
14538
14711
|
var REQUIRED_RESOURCE_ACCESS = [
|
|
@@ -14547,10 +14720,8 @@ var REQUIRED_RESOURCE_ACCESS = [
|
|
|
14547
14720
|
{
|
|
14548
14721
|
resourceAppId: "7df0a125-d3be-4c96-aa54-591f83ff541c",
|
|
14549
14722
|
resourceAccess: [
|
|
14550
|
-
{ id: "
|
|
14551
|
-
{ id: "
|
|
14552
|
-
{ id: "822a9cde-503a-472d-a530-d1dc9cd0d52b", type: "Scope" },
|
|
14553
|
-
{ id: "d05743e4-fb24-4dff-8a33-0f6c73c964bd", type: "Scope" }
|
|
14723
|
+
{ id: "e07c4438-06fe-4349-9077-b8ab4e888e21", type: "Scope" },
|
|
14724
|
+
{ id: "f3a53f5e-1975-4e99-8c6e-c6a0dfac61f6", type: "Scope" }
|
|
14554
14725
|
]
|
|
14555
14726
|
},
|
|
14556
14727
|
{
|
|
@@ -14638,8 +14809,6 @@ async function createAppRegistration(displayName) {
|
|
|
14638
14809
|
"false",
|
|
14639
14810
|
"--enable-id-token-issuance",
|
|
14640
14811
|
"false",
|
|
14641
|
-
"--is-fallback-public-client",
|
|
14642
|
-
"true",
|
|
14643
14812
|
"--public-client-redirect-uris",
|
|
14644
14813
|
REDIRECT_URI,
|
|
14645
14814
|
"--required-resource-accesses",
|
|
@@ -14737,82 +14906,75 @@ function clearTokenCache() {
|
|
|
14737
14906
|
return { cleared: files.length > 0, files };
|
|
14738
14907
|
}
|
|
14739
14908
|
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
|
|
14909
|
+
function readExistingAuthConfig() {
|
|
14741
14910
|
try {
|
|
14742
14911
|
const configPath = getConfigPath();
|
|
14743
|
-
if (!existsSync(configPath)) return null;
|
|
14912
|
+
if (!existsSync(configPath)) return { clientId: null, tenantId: null };
|
|
14744
14913
|
const raw = readFileSync(configPath, "utf-8");
|
|
14745
14914
|
const parsed = JSON.parse(raw);
|
|
14746
|
-
const
|
|
14747
|
-
|
|
14748
|
-
return
|
|
14915
|
+
const clientId = parsed?.auth?.clientId;
|
|
14916
|
+
const tenantId = parsed?.auth?.tenantId;
|
|
14917
|
+
return {
|
|
14918
|
+
clientId: clientId && UUID_RE2.test(clientId) ? clientId : null,
|
|
14919
|
+
tenantId: tenantId && UUID_RE2.test(tenantId) ? tenantId : null
|
|
14920
|
+
};
|
|
14749
14921
|
} catch {
|
|
14750
|
-
return null;
|
|
14922
|
+
return { clientId: null, tenantId: null };
|
|
14751
14923
|
}
|
|
14752
14924
|
}
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
print(` ${c.yellow}You need a Microsoft Entra app registration for your tenant.${c.reset}`);
|
|
14756
|
-
print(` ${c.dim}Create one in the Azure portal with these delegated permissions:${c.reset}`);
|
|
14757
|
-
print(` ${c.dim}\u2022 Microsoft Graph: User.Read, Sites.ReadWrite.All, Files.ReadWrite.All${c.reset}`);
|
|
14758
|
-
print(` ${c.dim}\u2022 Power Automate: Flows.Read.All, Flows.Manage.All, Activity.Read.All, Approvals.Manage.All${c.reset}`);
|
|
14759
|
-
print(` ${c.dim}\u2022 Dynamics CRM: user_impersonation${c.reset}`);
|
|
14760
|
-
print(` ${c.dim}Enable "Allow public client flows" for device code auth.${c.reset}`);
|
|
14761
|
-
print("");
|
|
14762
|
-
while (true) {
|
|
14763
|
-
const input = await askUser(` ${c.cyan}?${c.reset} Paste your app's ${c.bold}Client ID${c.reset}: `);
|
|
14764
|
-
if (input && UUID_RE2.test(input)) {
|
|
14765
|
-
return input;
|
|
14766
|
-
}
|
|
14767
|
-
print(` ${c.red}Invalid format \u2014 must be a UUID (e.g., 12345678-abcd-1234-abcd-123456789abc)${c.reset}`);
|
|
14768
|
-
}
|
|
14925
|
+
function readExistingClientId() {
|
|
14926
|
+
return readExistingAuthConfig().clientId;
|
|
14769
14927
|
}
|
|
14770
|
-
async function resolveAppRegistration(
|
|
14771
|
-
const envClientId = process.env["PA_MCP_CLIENT_ID"];
|
|
14772
|
-
if (envClientId && UUID_RE2.test(envClientId)) {
|
|
14773
|
-
print(` ${icons.check} Using client ID from PA_MCP_CLIENT_ID: ${c.dim}${envClientId}${c.reset}`);
|
|
14774
|
-
return { clientId: envClientId, created: false };
|
|
14775
|
-
}
|
|
14928
|
+
async function resolveAppRegistration() {
|
|
14776
14929
|
const existingId = readExistingClientId();
|
|
14777
14930
|
if (existingId) {
|
|
14778
14931
|
print(` ${icons.check} Reusing app from existing config: ${c.dim}${existingId}${c.reset}`);
|
|
14779
14932
|
return { clientId: existingId, created: false };
|
|
14780
14933
|
}
|
|
14781
14934
|
const hasAzCli = await checkAzureCli();
|
|
14782
|
-
if (hasAzCli) {
|
|
14783
|
-
print(` ${c.dim}Azure CLI
|
|
14935
|
+
if (!hasAzCli) {
|
|
14936
|
+
print(` ${c.dim}Azure CLI not found \u2014 using shared app${c.reset}`);
|
|
14937
|
+
print(` ${icons.check} Client ID: ${c.dim}${PUBLISHED_APP_CLIENT_ID}${c.reset}`);
|
|
14938
|
+
return { clientId: PUBLISHED_APP_CLIENT_ID, created: false };
|
|
14939
|
+
}
|
|
14940
|
+
print(` ${c.dim}Azure CLI detected${c.reset}`);
|
|
14941
|
+
try {
|
|
14942
|
+
const azUser = await ensureAzureCliLogin();
|
|
14943
|
+
print(` ${icons.check} Azure CLI signed in as ${c.bold}${azUser}${c.reset}`);
|
|
14944
|
+
} catch (error) {
|
|
14945
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
14946
|
+
print(` ${c.yellow}Azure CLI login failed: ${msg}${c.reset}`);
|
|
14947
|
+
print(` ${c.dim}Falling back to shared app${c.reset}`);
|
|
14948
|
+
print(` ${icons.check} Client ID: ${c.dim}${PUBLISHED_APP_CLIENT_ID}${c.reset}`);
|
|
14949
|
+
return { clientId: PUBLISHED_APP_CLIENT_ID, created: false };
|
|
14950
|
+
}
|
|
14951
|
+
const existingAppId = await findExistingApp(APP_DISPLAY_NAME);
|
|
14952
|
+
if (existingAppId) {
|
|
14953
|
+
print(` ${icons.check} Found existing app: ${c.dim}${existingAppId}${c.reset}`);
|
|
14784
14954
|
try {
|
|
14785
|
-
|
|
14786
|
-
|
|
14787
|
-
const existingAppId = await findExistingApp(APP_DISPLAY_NAME);
|
|
14788
|
-
if (existingAppId) {
|
|
14789
|
-
print(` ${icons.check} Found existing app: ${c.dim}${existingAppId}${c.reset}`);
|
|
14790
|
-
try {
|
|
14791
|
-
await addWamRedirectUri(existingAppId);
|
|
14792
|
-
} catch {
|
|
14793
|
-
}
|
|
14794
|
-
return { clientId: existingAppId, created: false };
|
|
14795
|
-
}
|
|
14796
|
-
print(` ${c.dim}Creating app registration...${c.reset}`);
|
|
14797
|
-
const newId = await createAppRegistration(APP_DISPLAY_NAME);
|
|
14798
|
-
print(` ${icons.check} App created: ${c.dim}${newId}${c.reset}`);
|
|
14799
|
-
try {
|
|
14800
|
-
await addWamRedirectUri(newId);
|
|
14801
|
-
print(` ${icons.check} WAM redirect URI configured`);
|
|
14802
|
-
} catch {
|
|
14803
|
-
print(` ${c.dim}WAM redirect URI skipped (non-critical)${c.reset}`);
|
|
14804
|
-
}
|
|
14805
|
-
return { clientId: newId, created: true };
|
|
14806
|
-
} catch (error) {
|
|
14807
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
14808
|
-
print(` ${c.yellow}Azure CLI failed: ${msg}${c.reset}`);
|
|
14955
|
+
await addWamRedirectUri(existingAppId);
|
|
14956
|
+
} catch {
|
|
14809
14957
|
}
|
|
14810
|
-
|
|
14811
|
-
|
|
14958
|
+
return { clientId: existingAppId, created: false };
|
|
14959
|
+
}
|
|
14960
|
+
print(` ${c.dim}Creating app registration...${c.reset}`);
|
|
14961
|
+
try {
|
|
14962
|
+
const newId = await createAppRegistration(APP_DISPLAY_NAME);
|
|
14963
|
+
print(` ${icons.check} App created: ${c.dim}${newId}${c.reset}`);
|
|
14964
|
+
try {
|
|
14965
|
+
await addWamRedirectUri(newId);
|
|
14966
|
+
print(` ${icons.check} WAM redirect URI configured`);
|
|
14967
|
+
} catch {
|
|
14968
|
+
print(` ${c.dim}WAM redirect URI skipped (non-critical)${c.reset}`);
|
|
14969
|
+
}
|
|
14970
|
+
return { clientId: newId, created: true };
|
|
14971
|
+
} catch (error) {
|
|
14972
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
14973
|
+
print(` ${c.yellow}App creation failed: ${msg}${c.reset}`);
|
|
14974
|
+
print(` ${c.dim}Falling back to shared app${c.reset}`);
|
|
14975
|
+
print(` ${icons.check} Client ID: ${c.dim}${PUBLISHED_APP_CLIENT_ID}${c.reset}`);
|
|
14976
|
+
return { clientId: PUBLISHED_APP_CLIENT_ID, created: false };
|
|
14812
14977
|
}
|
|
14813
|
-
const manualId = await promptForClientId(askUser);
|
|
14814
|
-
print(` ${icons.check} Client ID: ${c.dim}${manualId}${c.reset}`);
|
|
14815
|
-
return { clientId: manualId, created: false };
|
|
14816
14978
|
}
|
|
14817
14979
|
async function runSetupWizard() {
|
|
14818
14980
|
const rl = createInterface({
|
|
@@ -14830,7 +14992,7 @@ async function runSetupWizard() {
|
|
|
14830
14992
|
printHeader();
|
|
14831
14993
|
const totalSteps = 5;
|
|
14832
14994
|
printStep(1, totalSteps, "App Registration");
|
|
14833
|
-
const appResult = await resolveAppRegistration(
|
|
14995
|
+
const appResult = await resolveAppRegistration();
|
|
14834
14996
|
const clientId = appResult.clientId;
|
|
14835
14997
|
printStep(2, totalSteps, "Sign In");
|
|
14836
14998
|
const cacheResult = clearTokenCache();
|
|
@@ -14840,8 +15002,9 @@ async function runSetupWizard() {
|
|
|
14840
15002
|
print(` ${c.dim}Sign in with your Microsoft work account.${c.reset}`);
|
|
14841
15003
|
print(` ${c.dim}A device code will be shown \u2014 follow the instructions to authenticate.${c.reset}`);
|
|
14842
15004
|
print("");
|
|
14843
|
-
const
|
|
14844
|
-
let tenantId =
|
|
15005
|
+
const existingAuth = readExistingAuthConfig();
|
|
15006
|
+
let tenantId = existingAuth.tenantId;
|
|
15007
|
+
const authProvider = await createAuthProviderFromConfig2(clientId, tenantId);
|
|
14845
15008
|
try {
|
|
14846
15009
|
await authProvider.getToken();
|
|
14847
15010
|
const account = authProvider.getCurrentAccount();
|
|
@@ -14997,6 +15160,71 @@ function isSetupNeeded() {
|
|
|
14997
15160
|
return !existsSync(configPath);
|
|
14998
15161
|
}
|
|
14999
15162
|
|
|
15163
|
+
// src/cli/validate.ts
|
|
15164
|
+
async function runValidate() {
|
|
15165
|
+
console.error("Validating Power Automate MCP configuration...\n");
|
|
15166
|
+
let config;
|
|
15167
|
+
try {
|
|
15168
|
+
const configPath = getConfigPath();
|
|
15169
|
+
console.error(` [1/3] Config file: ${configPath}`);
|
|
15170
|
+
config = loadConfig();
|
|
15171
|
+
const envNames = Object.keys(config.environments).filter((k) => k !== "default");
|
|
15172
|
+
console.error(` Client ID: ${config.auth.clientId}`);
|
|
15173
|
+
console.error(` Tenant: ${config.auth.tenantId ?? "common"}`);
|
|
15174
|
+
console.error(` Environments: ${envNames.join(", ") || "(none)"}`);
|
|
15175
|
+
console.error(` Default: ${config.environments.default}`);
|
|
15176
|
+
console.error(" \u2713 Config valid\n");
|
|
15177
|
+
} catch (err) {
|
|
15178
|
+
const msg = err instanceof ConfigurationError ? err.message : String(err);
|
|
15179
|
+
console.error(` \u2717 Config error: ${msg}
|
|
15180
|
+
`);
|
|
15181
|
+
process.exit(1);
|
|
15182
|
+
}
|
|
15183
|
+
let authProvider;
|
|
15184
|
+
try {
|
|
15185
|
+
console.error(" [2/3] Authenticating...");
|
|
15186
|
+
authProvider = await createAuthProviderFromConfig(
|
|
15187
|
+
config.auth.clientId,
|
|
15188
|
+
config.auth.tenantId,
|
|
15189
|
+
void 0,
|
|
15190
|
+
true
|
|
15191
|
+
// silentOnly
|
|
15192
|
+
);
|
|
15193
|
+
const token = await authProvider.getToken();
|
|
15194
|
+
console.error(` Token acquired (${token.length} chars)`);
|
|
15195
|
+
const account = authProvider.getCurrentAccount();
|
|
15196
|
+
if (account) {
|
|
15197
|
+
console.error(` Account: ${account.username ?? account.name ?? "unknown"}`);
|
|
15198
|
+
}
|
|
15199
|
+
console.error(" \u2713 Auth valid\n");
|
|
15200
|
+
} catch (err) {
|
|
15201
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15202
|
+
console.error(` \u2717 Auth error: ${msg}`);
|
|
15203
|
+
console.error(" Run: powerautomate-mcp --setup\n");
|
|
15204
|
+
process.exit(1);
|
|
15205
|
+
}
|
|
15206
|
+
try {
|
|
15207
|
+
console.error(" [3/3] Testing API connectivity...");
|
|
15208
|
+
const defaultEnv = resolveEnvironment(config);
|
|
15209
|
+
const flowApi = new FlowManagementApi({
|
|
15210
|
+
authProvider,
|
|
15211
|
+
defaultEnvironmentId: defaultEnv.id
|
|
15212
|
+
});
|
|
15213
|
+
const result = await flowApi.listFlows({ environment: defaultEnv.id, top: 1 });
|
|
15214
|
+
const count = result.flows.length;
|
|
15215
|
+
console.error(` Environment: ${defaultEnv.name} (${defaultEnv.id})`);
|
|
15216
|
+
console.error(` Flow API reachable (${count >= 1 ? "flows found" : "connected"})`);
|
|
15217
|
+
console.error(" \u2713 Connectivity valid\n");
|
|
15218
|
+
} catch (err) {
|
|
15219
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
15220
|
+
console.error(` \u2717 Connectivity error: ${msg}
|
|
15221
|
+
`);
|
|
15222
|
+
process.exit(1);
|
|
15223
|
+
}
|
|
15224
|
+
console.error("All checks passed. Server is ready to run.\n");
|
|
15225
|
+
process.exit(0);
|
|
15226
|
+
}
|
|
15227
|
+
|
|
15000
15228
|
// src/index.ts
|
|
15001
15229
|
function parseArgs() {
|
|
15002
15230
|
const args = process.argv.slice(2);
|
|
@@ -15008,11 +15236,27 @@ function parseArgs() {
|
|
|
15008
15236
|
port = parsed;
|
|
15009
15237
|
}
|
|
15010
15238
|
}
|
|
15239
|
+
let env2;
|
|
15240
|
+
const envIdx = args.indexOf("--env");
|
|
15241
|
+
if (envIdx !== -1 && args[envIdx + 1] && !args[envIdx + 1].startsWith("-")) {
|
|
15242
|
+
env2 = args[envIdx + 1];
|
|
15243
|
+
}
|
|
15244
|
+
let config;
|
|
15245
|
+
const configIdx = args.indexOf("--config");
|
|
15246
|
+
if (configIdx !== -1 && args[configIdx + 1] && !args[configIdx + 1].startsWith("-")) {
|
|
15247
|
+
config = args[configIdx + 1];
|
|
15248
|
+
}
|
|
15011
15249
|
return {
|
|
15012
15250
|
setup: args.includes("--setup") || args.includes("-s"),
|
|
15013
15251
|
help: args.includes("--help") || args.includes("-h"),
|
|
15252
|
+
version: args.includes("--version") || args.includes("-v"),
|
|
15253
|
+
validate: args.includes("--validate"),
|
|
15254
|
+
update: args.includes("--update"),
|
|
15014
15255
|
http: args.includes("--http"),
|
|
15015
|
-
port
|
|
15256
|
+
port,
|
|
15257
|
+
env: env2,
|
|
15258
|
+
config,
|
|
15259
|
+
debug: args.includes("--debug")
|
|
15016
15260
|
};
|
|
15017
15261
|
}
|
|
15018
15262
|
function getConfigPathHint() {
|
|
@@ -15026,28 +15270,28 @@ function getConfigPathHint() {
|
|
|
15026
15270
|
}
|
|
15027
15271
|
function printHelp() {
|
|
15028
15272
|
const configPath = getConfigPathHint();
|
|
15273
|
+
const version = getInstalledVersion();
|
|
15029
15274
|
console.log(`
|
|
15030
|
-
Power Automate MCP Server
|
|
15275
|
+
Power Automate MCP Server v${version}
|
|
15031
15276
|
|
|
15032
15277
|
Usage: powerautomate-mcp [options]
|
|
15033
15278
|
|
|
15034
15279
|
Options:
|
|
15035
|
-
--setup, -s
|
|
15036
|
-
--
|
|
15037
|
-
--
|
|
15038
|
-
--
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15280
|
+
--setup, -s Run the interactive setup wizard
|
|
15281
|
+
--validate Verify config, auth, and API connectivity then exit
|
|
15282
|
+
--update Check for updates and install the latest version
|
|
15283
|
+
--version, -v Print version and exit
|
|
15284
|
+
--http Start with Streamable HTTP transport (for ChatGPT / remote clients)
|
|
15285
|
+
--port <N> Port for HTTP transport (default: 3000)
|
|
15286
|
+
--env <name> Override the default environment (alias or GUID)
|
|
15287
|
+
--config <path> Use an alternate config file
|
|
15288
|
+
--debug Enable debug-level logging
|
|
15289
|
+
--help, -h Show this help message
|
|
15043
15290
|
|
|
15044
15291
|
First Run:
|
|
15045
15292
|
If no configuration exists, the setup wizard will run automatically.
|
|
15046
15293
|
You can also run it manually with --setup.
|
|
15047
15294
|
|
|
15048
|
-
Each tenant needs its own Microsoft Entra app registration.
|
|
15049
|
-
The wizard can create one via Azure CLI, or you can provide one manually.
|
|
15050
|
-
|
|
15051
15295
|
Configuration:
|
|
15052
15296
|
Config file: ${configPath}
|
|
15053
15297
|
|
|
@@ -15056,10 +15300,30 @@ For more information, see the README.md file.
|
|
|
15056
15300
|
}
|
|
15057
15301
|
async function main() {
|
|
15058
15302
|
const args = parseArgs();
|
|
15303
|
+
if (args.version) {
|
|
15304
|
+
console.log(getInstalledVersion());
|
|
15305
|
+
process.exit(0);
|
|
15306
|
+
}
|
|
15059
15307
|
if (args.help) {
|
|
15060
15308
|
printHelp();
|
|
15061
15309
|
process.exit(0);
|
|
15062
15310
|
}
|
|
15311
|
+
if (args.update) {
|
|
15312
|
+
await runSelfUpdate();
|
|
15313
|
+
return;
|
|
15314
|
+
}
|
|
15315
|
+
if (args.debug) {
|
|
15316
|
+
logger.level = "debug";
|
|
15317
|
+
logger.debug("Debug logging enabled via --debug");
|
|
15318
|
+
}
|
|
15319
|
+
if (args.config) {
|
|
15320
|
+
process.env["PA_CONFIG_PATH"] = args.config;
|
|
15321
|
+
logger.debug({ configPath: args.config }, "Using custom config path");
|
|
15322
|
+
}
|
|
15323
|
+
if (args.validate) {
|
|
15324
|
+
await runValidate();
|
|
15325
|
+
return;
|
|
15326
|
+
}
|
|
15063
15327
|
if (args.setup || isSetupNeeded()) {
|
|
15064
15328
|
const result = await runSetupWizard();
|
|
15065
15329
|
if (!result.success) {
|
|
@@ -15072,7 +15336,11 @@ Setup failed: ${result.error}`);
|
|
|
15072
15336
|
process.exit(0);
|
|
15073
15337
|
}
|
|
15074
15338
|
}
|
|
15075
|
-
logger.info(
|
|
15339
|
+
logger.info(
|
|
15340
|
+
{ platform: process.platform, nodeVersion: process.version, version: getInstalledVersion() },
|
|
15341
|
+
"Starting Power Automate MCP"
|
|
15342
|
+
);
|
|
15343
|
+
backgroundUpdateCheck();
|
|
15076
15344
|
let config;
|
|
15077
15345
|
try {
|
|
15078
15346
|
config = loadConfig();
|
|
@@ -15089,6 +15357,10 @@ Configuration Error: ${error.message}
|
|
|
15089
15357
|
}
|
|
15090
15358
|
throw error;
|
|
15091
15359
|
}
|
|
15360
|
+
if (args.env) {
|
|
15361
|
+
config.environments.default = args.env;
|
|
15362
|
+
logger.info({ env: args.env }, "Default environment overridden via --env");
|
|
15363
|
+
}
|
|
15092
15364
|
try {
|
|
15093
15365
|
const { start } = await createMcpServer({
|
|
15094
15366
|
config,
|