powerautomate-mcp 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +308 -57
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -259,7 +259,7 @@ function createChildLogger(bindings) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
// src/utils/constants.ts
|
|
262
|
-
var POWER_PLATFORM_API_BASE = "https://api.
|
|
262
|
+
var POWER_PLATFORM_API_BASE = "https://api.bap.microsoft.com";
|
|
263
263
|
var FLOW_API_BASE = "https://api.flow.microsoft.com";
|
|
264
264
|
var POWERAPPS_API_BASE = "https://api.powerapps.com";
|
|
265
265
|
var POWER_PLATFORM_API_VERSION = "2022-03-01-preview";
|
|
@@ -283,7 +283,9 @@ var ALLOWED_TOKEN_DOMAINS = [
|
|
|
283
283
|
"sharepoint.com",
|
|
284
284
|
"office.com",
|
|
285
285
|
"azure.com",
|
|
286
|
-
"windows.net"
|
|
286
|
+
"windows.net",
|
|
287
|
+
"powerplatform.com",
|
|
288
|
+
"powerapps.com"
|
|
287
289
|
];
|
|
288
290
|
function isAllowedTokenDomain(hostname) {
|
|
289
291
|
const lower = hostname.toLowerCase().replace(/\.+$/, "");
|
|
@@ -382,6 +384,20 @@ Create the file with your Microsoft Entra app client ID and Power Automate envir
|
|
|
382
384
|
);
|
|
383
385
|
}
|
|
384
386
|
parsed = interpolateEnvVars(parsed);
|
|
387
|
+
const envClientId = env["PA_MCP_CLIENT_ID"];
|
|
388
|
+
if (envClientId) {
|
|
389
|
+
const UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
390
|
+
if (!UUID_RE3.test(envClientId)) {
|
|
391
|
+
throw new ConfigurationError(
|
|
392
|
+
`PA_MCP_CLIENT_ID is not a valid UUID: ${envClientId.substring(0, 50)}`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
parsed.auth = {
|
|
396
|
+
...parsed.auth ?? {},
|
|
397
|
+
clientId: envClientId
|
|
398
|
+
};
|
|
399
|
+
logger.info("Using client ID from PA_MCP_CLIENT_ID environment variable");
|
|
400
|
+
}
|
|
385
401
|
try {
|
|
386
402
|
const config = validateConfig(parsed);
|
|
387
403
|
logger.info(
|
|
@@ -1960,7 +1976,7 @@ var ConnectorMetadataApi = class {
|
|
|
1960
1976
|
var RESOURCES = {
|
|
1961
1977
|
FLOW_SERVICE: "https://service.flow.microsoft.com",
|
|
1962
1978
|
POWERAPPS_SERVICE: "https://service.powerapps.com",
|
|
1963
|
-
POWER_PLATFORM: "https://api.
|
|
1979
|
+
POWER_PLATFORM: "https://api.bap.microsoft.com",
|
|
1964
1980
|
GRAPH: "https://graph.microsoft.com"
|
|
1965
1981
|
};
|
|
1966
1982
|
|
|
@@ -2735,7 +2751,14 @@ var PowerPlatformAdminApi = class {
|
|
|
2735
2751
|
};
|
|
2736
2752
|
}
|
|
2737
2753
|
buildUrl(path, queryParams) {
|
|
2738
|
-
|
|
2754
|
+
let resolvedPath = path;
|
|
2755
|
+
if (path.startsWith("/providers/") && !path.includes("/scopes/admin/")) {
|
|
2756
|
+
resolvedPath = path.replace(
|
|
2757
|
+
"/providers/Microsoft.BusinessAppPlatform/",
|
|
2758
|
+
"/providers/Microsoft.BusinessAppPlatform/scopes/admin/"
|
|
2759
|
+
);
|
|
2760
|
+
}
|
|
2761
|
+
const base = `${POWER_PLATFORM_API_BASE}${resolvedPath}`;
|
|
2739
2762
|
const params = new URLSearchParams();
|
|
2740
2763
|
params.set("api-version", POWER_PLATFORM_API_VERSION);
|
|
2741
2764
|
if (queryParams) {
|
|
@@ -5009,15 +5032,52 @@ var KNOWN_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
|
5009
5032
|
"uriQuery",
|
|
5010
5033
|
"uriScheme"
|
|
5011
5034
|
]);
|
|
5012
|
-
|
|
5035
|
+
function extractWrappedExpressions(text) {
|
|
5036
|
+
const expressions = [];
|
|
5037
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
5038
|
+
if (text[i] !== "@") continue;
|
|
5039
|
+
if (text[i + 1] !== "{") continue;
|
|
5040
|
+
if (i > 0 && text[i - 1] === "@") continue;
|
|
5041
|
+
let depth = 0;
|
|
5042
|
+
let inString = false;
|
|
5043
|
+
let stringChar = "";
|
|
5044
|
+
let end = -1;
|
|
5045
|
+
for (let j = i + 2; j < text.length; j++) {
|
|
5046
|
+
const char = text[j];
|
|
5047
|
+
const prevChar = j > i + 2 ? text[j - 1] : "";
|
|
5048
|
+
if ((char === "'" || char === '"') && prevChar !== "\\") {
|
|
5049
|
+
if (!inString) {
|
|
5050
|
+
inString = true;
|
|
5051
|
+
stringChar = char;
|
|
5052
|
+
} else if (char === stringChar) {
|
|
5053
|
+
inString = false;
|
|
5054
|
+
}
|
|
5055
|
+
continue;
|
|
5056
|
+
}
|
|
5057
|
+
if (inString) continue;
|
|
5058
|
+
if (char === "{") {
|
|
5059
|
+
depth++;
|
|
5060
|
+
continue;
|
|
5061
|
+
}
|
|
5062
|
+
if (char === "}") {
|
|
5063
|
+
if (depth === 0) {
|
|
5064
|
+
end = j;
|
|
5065
|
+
break;
|
|
5066
|
+
}
|
|
5067
|
+
depth--;
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
if (end !== -1) {
|
|
5071
|
+
expressions.push(text.slice(i + 2, end));
|
|
5072
|
+
i = end;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
return expressions;
|
|
5076
|
+
}
|
|
5013
5077
|
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;
|
|
5014
5078
|
function extractExpressions(text) {
|
|
5015
|
-
const expressions =
|
|
5079
|
+
const expressions = extractWrappedExpressions(text);
|
|
5016
5080
|
let match;
|
|
5017
|
-
while ((match = EXPRESSION_REGEX.exec(text)) !== null) {
|
|
5018
|
-
if (match[1]) expressions.push(match[1]);
|
|
5019
|
-
}
|
|
5020
|
-
EXPRESSION_REGEX.lastIndex = 0;
|
|
5021
5081
|
while ((match = BARE_EXPRESSION_REGEX.exec(text)) !== null) {
|
|
5022
5082
|
if (match[1]) expressions.push(match[1]);
|
|
5023
5083
|
}
|
|
@@ -5493,6 +5553,160 @@ function getAllValidationWarnings(result) {
|
|
|
5493
5553
|
return warnings;
|
|
5494
5554
|
}
|
|
5495
5555
|
|
|
5556
|
+
// src/utils/expression-escape.ts
|
|
5557
|
+
var logger3 = createChildLogger({ module: "expression-escape" });
|
|
5558
|
+
function containsExpressions(value) {
|
|
5559
|
+
if (typeof value === "string") {
|
|
5560
|
+
return value.includes("@{") || value.startsWith("@") && !value.startsWith("@@");
|
|
5561
|
+
}
|
|
5562
|
+
if (Array.isArray(value)) {
|
|
5563
|
+
return value.some(containsExpressions);
|
|
5564
|
+
}
|
|
5565
|
+
if (value && typeof value === "object") {
|
|
5566
|
+
return Object.values(value).some(containsExpressions);
|
|
5567
|
+
}
|
|
5568
|
+
return false;
|
|
5569
|
+
}
|
|
5570
|
+
function objectToStringExpression(obj) {
|
|
5571
|
+
const jsonStr = JSON.stringify(obj);
|
|
5572
|
+
const segments = [];
|
|
5573
|
+
let pos = 0;
|
|
5574
|
+
while (pos < jsonStr.length) {
|
|
5575
|
+
const exprStart = jsonStr.indexOf("@{", pos);
|
|
5576
|
+
if (exprStart === -1) {
|
|
5577
|
+
segments.push("'" + escapeForConcat(jsonStr.slice(pos)) + "'");
|
|
5578
|
+
break;
|
|
5579
|
+
}
|
|
5580
|
+
const isJsonStringValue = exprStart > 0 && jsonStr[exprStart - 1] === '"';
|
|
5581
|
+
if (exprStart > pos) {
|
|
5582
|
+
let literal = jsonStr.slice(pos, exprStart);
|
|
5583
|
+
if (isJsonStringValue) {
|
|
5584
|
+
literal = literal.slice(0, -1);
|
|
5585
|
+
}
|
|
5586
|
+
if (literal.length > 0) {
|
|
5587
|
+
segments.push("'" + escapeForConcat(literal) + "'");
|
|
5588
|
+
}
|
|
5589
|
+
}
|
|
5590
|
+
let depth = 0;
|
|
5591
|
+
let exprEnd = -1;
|
|
5592
|
+
for (let i = exprStart + 2; i < jsonStr.length; i++) {
|
|
5593
|
+
if (jsonStr[i] === "{") depth++;
|
|
5594
|
+
else if (jsonStr[i] === "}") {
|
|
5595
|
+
if (depth === 0) {
|
|
5596
|
+
exprEnd = i;
|
|
5597
|
+
break;
|
|
5598
|
+
}
|
|
5599
|
+
depth--;
|
|
5600
|
+
}
|
|
5601
|
+
}
|
|
5602
|
+
if (exprEnd === -1) {
|
|
5603
|
+
segments.push("'" + escapeForConcat(jsonStr.slice(exprStart)) + "'");
|
|
5604
|
+
break;
|
|
5605
|
+
}
|
|
5606
|
+
const expr = jsonStr.slice(exprStart + 2, exprEnd);
|
|
5607
|
+
if (isJsonStringValue) {
|
|
5608
|
+
segments.push(`'"'`);
|
|
5609
|
+
segments.push(expr);
|
|
5610
|
+
segments.push(`'"'`);
|
|
5611
|
+
} else {
|
|
5612
|
+
segments.push(expr);
|
|
5613
|
+
}
|
|
5614
|
+
pos = exprEnd + 1;
|
|
5615
|
+
if (isJsonStringValue && pos < jsonStr.length && jsonStr[pos] === '"') {
|
|
5616
|
+
pos++;
|
|
5617
|
+
}
|
|
5618
|
+
}
|
|
5619
|
+
const filtered = segments.filter((s) => s !== "''" && s !== "");
|
|
5620
|
+
if (filtered.length === 0) return jsonStr;
|
|
5621
|
+
if (filtered.length === 1 && !filtered[0].startsWith("'")) {
|
|
5622
|
+
return `@{${filtered[0]}}`;
|
|
5623
|
+
}
|
|
5624
|
+
return `@{json(concat(${filtered.join(",")}))}`;
|
|
5625
|
+
}
|
|
5626
|
+
function escapeForConcat(s) {
|
|
5627
|
+
return s.replace(/'/g, "''");
|
|
5628
|
+
}
|
|
5629
|
+
function preprocessActionInputs(inputs, actionName) {
|
|
5630
|
+
const processedInputs = { ...inputs };
|
|
5631
|
+
if (processedInputs.body && typeof processedInputs.body === "object") {
|
|
5632
|
+
const bodyStr = JSON.stringify(processedInputs.body);
|
|
5633
|
+
const hasAtKeys = /"@[a-zA-Z]/.test(bodyStr);
|
|
5634
|
+
if (hasAtKeys || containsExpressions(processedInputs.body)) {
|
|
5635
|
+
logger3.info({ action: actionName, field: "body", hasAtKeys }, "Converting action payload object to string expression");
|
|
5636
|
+
processedInputs.body = objectToStringExpression(processedInputs.body);
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
if (processedInputs.parameters && typeof processedInputs.parameters === "object" && !Array.isArray(processedInputs.parameters)) {
|
|
5640
|
+
const parameters = processedInputs.parameters;
|
|
5641
|
+
let changed = false;
|
|
5642
|
+
const processedParameters = { ...parameters };
|
|
5643
|
+
for (const [key, value] of Object.entries(parameters)) {
|
|
5644
|
+
if (value && typeof value === "object" && containsExpressions(value)) {
|
|
5645
|
+
logger3.info({ action: actionName, field: `parameters.${key}` }, "Converting connector parameter object to string expression");
|
|
5646
|
+
processedParameters[key] = objectToStringExpression(value);
|
|
5647
|
+
changed = true;
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
if (changed) {
|
|
5651
|
+
processedInputs.parameters = processedParameters;
|
|
5652
|
+
}
|
|
5653
|
+
}
|
|
5654
|
+
return processedInputs;
|
|
5655
|
+
}
|
|
5656
|
+
function preprocessFlowActions(actions) {
|
|
5657
|
+
const processed = {};
|
|
5658
|
+
for (const [name, action] of Object.entries(actions)) {
|
|
5659
|
+
if (!action || typeof action !== "object") {
|
|
5660
|
+
processed[name] = action;
|
|
5661
|
+
continue;
|
|
5662
|
+
}
|
|
5663
|
+
const actionObj = action;
|
|
5664
|
+
processed[name] = { ...actionObj };
|
|
5665
|
+
const processedAction = processed[name];
|
|
5666
|
+
if (actionObj.inputs && typeof actionObj.inputs === "object") {
|
|
5667
|
+
processedAction.inputs = preprocessActionInputs(
|
|
5668
|
+
actionObj.inputs,
|
|
5669
|
+
name
|
|
5670
|
+
);
|
|
5671
|
+
}
|
|
5672
|
+
if (actionObj.actions && typeof actionObj.actions === "object") {
|
|
5673
|
+
processedAction.actions = preprocessFlowActions(
|
|
5674
|
+
actionObj.actions
|
|
5675
|
+
);
|
|
5676
|
+
}
|
|
5677
|
+
if (actionObj.else && typeof actionObj.else === "object") {
|
|
5678
|
+
const elseBranch = actionObj.else;
|
|
5679
|
+
if (elseBranch.actions && typeof elseBranch.actions === "object") {
|
|
5680
|
+
processedAction.else = {
|
|
5681
|
+
...elseBranch,
|
|
5682
|
+
actions: preprocessFlowActions(elseBranch.actions)
|
|
5683
|
+
};
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
if (actionObj.cases && typeof actionObj.cases === "object") {
|
|
5687
|
+
const cases = actionObj.cases;
|
|
5688
|
+
const processedCases = {};
|
|
5689
|
+
for (const [caseName, caseVal] of Object.entries(cases)) {
|
|
5690
|
+
if (caseVal && typeof caseVal === "object") {
|
|
5691
|
+
const cv = caseVal;
|
|
5692
|
+
if (cv.actions && typeof cv.actions === "object") {
|
|
5693
|
+
processedCases[caseName] = {
|
|
5694
|
+
...cv,
|
|
5695
|
+
actions: preprocessFlowActions(cv.actions)
|
|
5696
|
+
};
|
|
5697
|
+
} else {
|
|
5698
|
+
processedCases[caseName] = caseVal;
|
|
5699
|
+
}
|
|
5700
|
+
} else {
|
|
5701
|
+
processedCases[caseName] = caseVal;
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
processedAction.cases = processedCases;
|
|
5705
|
+
}
|
|
5706
|
+
}
|
|
5707
|
+
return processed;
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5496
5710
|
// src/tools/create-flow.ts
|
|
5497
5711
|
var triggerSchema = z.object({
|
|
5498
5712
|
type: z.string().describe("Trigger type (e.g., 'Request', 'Recurrence', 'OpenApiConnection')"),
|
|
@@ -5601,13 +5815,14 @@ var createFlowTool = {
|
|
|
5601
5815
|
async function handleCreateFlow(api, input) {
|
|
5602
5816
|
const parsed = createFlowInputSchema.parse(input);
|
|
5603
5817
|
logger.info({ displayName: parsed.displayName }, "Executing create_flow");
|
|
5818
|
+
const processedActions = preprocessFlowActions(parsed.actions);
|
|
5604
5819
|
const definition = {
|
|
5605
5820
|
$schema: "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
|
|
5606
5821
|
contentVersion: "1.0.0.0",
|
|
5607
5822
|
triggers: {
|
|
5608
5823
|
[parsed.triggerName ?? "manual"]: parsed.trigger
|
|
5609
5824
|
},
|
|
5610
|
-
actions:
|
|
5825
|
+
actions: processedActions
|
|
5611
5826
|
};
|
|
5612
5827
|
if (parsed.connectionReferences && Object.keys(parsed.connectionReferences).length > 0) {
|
|
5613
5828
|
definition.parameters = {
|
|
@@ -5782,11 +5997,12 @@ async function handleUpdateFlow(api, input) {
|
|
|
5782
5997
|
let newDefinition;
|
|
5783
5998
|
if (parsed.trigger || parsed.actions) {
|
|
5784
5999
|
const triggerName = parsed.triggerName ?? Object.keys(currentDef.triggers)[0] ?? "manual";
|
|
6000
|
+
const processedActions = parsed.actions ? preprocessFlowActions(parsed.actions) : currentDef.actions;
|
|
5785
6001
|
newDefinition = {
|
|
5786
6002
|
$schema: currentDef.$schema,
|
|
5787
6003
|
contentVersion: currentDef.contentVersion,
|
|
5788
6004
|
triggers: parsed.trigger ? { [triggerName]: parsed.trigger } : currentDef.triggers,
|
|
5789
|
-
actions:
|
|
6005
|
+
actions: processedActions
|
|
5790
6006
|
};
|
|
5791
6007
|
if (currentDef.parameters) {
|
|
5792
6008
|
newDefinition.parameters = currentDef.parameters;
|
|
@@ -14705,7 +14921,6 @@ function getDefaultEnvironment(environments) {
|
|
|
14705
14921
|
}
|
|
14706
14922
|
|
|
14707
14923
|
// src/setup/published-app.ts
|
|
14708
|
-
var PUBLISHED_APP_CLIENT_ID = "20ae33ee-6743-4786-9b00-8eb7bfebfd11";
|
|
14709
14924
|
var APP_DISPLAY_NAME = "Power Automate MCP";
|
|
14710
14925
|
var REDIRECT_URI = "https://login.microsoftonline.com/common/oauth2/nativeclient";
|
|
14711
14926
|
var REQUIRED_RESOURCE_ACCESS = [
|
|
@@ -14720,8 +14935,10 @@ var REQUIRED_RESOURCE_ACCESS = [
|
|
|
14720
14935
|
{
|
|
14721
14936
|
resourceAppId: "7df0a125-d3be-4c96-aa54-591f83ff541c",
|
|
14722
14937
|
resourceAccess: [
|
|
14723
|
-
{ id: "
|
|
14724
|
-
{ id: "
|
|
14938
|
+
{ id: "e45c5562-459d-4d1b-8148-83eb1b6dcf83", type: "Scope" },
|
|
14939
|
+
{ id: "30b2d850-00c3-4802-b7ae-ece9af9de5c6", type: "Scope" },
|
|
14940
|
+
{ id: "822a9cde-503a-472d-a530-d1dc9cd0d52b", type: "Scope" },
|
|
14941
|
+
{ id: "d05743e4-fb24-4dff-8a33-0f6c73c964bd", type: "Scope" }
|
|
14725
14942
|
]
|
|
14726
14943
|
},
|
|
14727
14944
|
{
|
|
@@ -14809,6 +15026,8 @@ async function createAppRegistration(displayName) {
|
|
|
14809
15026
|
"false",
|
|
14810
15027
|
"--enable-id-token-issuance",
|
|
14811
15028
|
"false",
|
|
15029
|
+
"--is-fallback-public-client",
|
|
15030
|
+
"true",
|
|
14812
15031
|
"--public-client-redirect-uris",
|
|
14813
15032
|
REDIRECT_URI,
|
|
14814
15033
|
"--required-resource-accesses",
|
|
@@ -14925,56 +15144,69 @@ function readExistingAuthConfig() {
|
|
|
14925
15144
|
function readExistingClientId() {
|
|
14926
15145
|
return readExistingAuthConfig().clientId;
|
|
14927
15146
|
}
|
|
14928
|
-
async function
|
|
15147
|
+
async function promptForClientId(askUser) {
|
|
15148
|
+
print("");
|
|
15149
|
+
print(` ${c.yellow}You need a Microsoft Entra app registration for your tenant.${c.reset}`);
|
|
15150
|
+
print(` ${c.dim}Create one in the Azure portal with these delegated permissions:${c.reset}`);
|
|
15151
|
+
print(` ${c.dim}\u2022 Microsoft Graph: User.Read, Sites.ReadWrite.All, Files.ReadWrite.All${c.reset}`);
|
|
15152
|
+
print(` ${c.dim}\u2022 Power Automate: Flows.Read.All, Flows.Manage.All, Activity.Read.All, Approvals.Manage.All${c.reset}`);
|
|
15153
|
+
print(` ${c.dim}\u2022 Dynamics CRM: user_impersonation${c.reset}`);
|
|
15154
|
+
print(` ${c.dim}Enable "Allow public client flows" for device code auth.${c.reset}`);
|
|
15155
|
+
print("");
|
|
15156
|
+
while (true) {
|
|
15157
|
+
const input = await askUser(` ${c.cyan}?${c.reset} Paste your app's ${c.bold}Client ID${c.reset}: `);
|
|
15158
|
+
if (input && UUID_RE2.test(input)) {
|
|
15159
|
+
return input;
|
|
15160
|
+
}
|
|
15161
|
+
print(` ${c.red}Invalid format \u2014 must be a UUID (e.g., 12345678-abcd-1234-abcd-123456789abc)${c.reset}`);
|
|
15162
|
+
}
|
|
15163
|
+
}
|
|
15164
|
+
async function resolveAppRegistration(askUser) {
|
|
15165
|
+
const envClientId = process.env["PA_MCP_CLIENT_ID"];
|
|
15166
|
+
if (envClientId && UUID_RE2.test(envClientId)) {
|
|
15167
|
+
print(` ${icons.check} Using client ID from PA_MCP_CLIENT_ID: ${c.dim}${envClientId}${c.reset}`);
|
|
15168
|
+
return { clientId: envClientId, created: false };
|
|
15169
|
+
}
|
|
14929
15170
|
const existingId = readExistingClientId();
|
|
14930
15171
|
if (existingId) {
|
|
14931
15172
|
print(` ${icons.check} Reusing app from existing config: ${c.dim}${existingId}${c.reset}`);
|
|
14932
15173
|
return { clientId: existingId, created: false };
|
|
14933
15174
|
}
|
|
14934
15175
|
const hasAzCli = await checkAzureCli();
|
|
14935
|
-
if (
|
|
14936
|
-
print(` ${c.dim}Azure CLI
|
|
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}`);
|
|
15176
|
+
if (hasAzCli) {
|
|
15177
|
+
print(` ${c.dim}Azure CLI detected${c.reset}`);
|
|
14954
15178
|
try {
|
|
14955
|
-
await
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
}
|
|
14960
|
-
|
|
14961
|
-
|
|
14962
|
-
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
print(` ${
|
|
14967
|
-
|
|
14968
|
-
print(` ${
|
|
15179
|
+
const azUser = await ensureAzureCliLogin();
|
|
15180
|
+
print(` ${icons.check} Azure CLI signed in as ${c.bold}${azUser}${c.reset}`);
|
|
15181
|
+
const existingAppId = await findExistingApp(APP_DISPLAY_NAME);
|
|
15182
|
+
if (existingAppId) {
|
|
15183
|
+
print(` ${icons.check} Found existing app: ${c.dim}${existingAppId}${c.reset}`);
|
|
15184
|
+
try {
|
|
15185
|
+
await addWamRedirectUri(existingAppId);
|
|
15186
|
+
} catch {
|
|
15187
|
+
}
|
|
15188
|
+
return { clientId: existingAppId, created: false };
|
|
15189
|
+
}
|
|
15190
|
+
print(` ${c.dim}Creating app registration...${c.reset}`);
|
|
15191
|
+
const newId = await createAppRegistration(APP_DISPLAY_NAME);
|
|
15192
|
+
print(` ${icons.check} App created: ${c.dim}${newId}${c.reset}`);
|
|
15193
|
+
try {
|
|
15194
|
+
await addWamRedirectUri(newId);
|
|
15195
|
+
print(` ${icons.check} WAM redirect URI configured`);
|
|
15196
|
+
} catch {
|
|
15197
|
+
print(` ${c.dim}WAM redirect URI skipped (non-critical)${c.reset}`);
|
|
15198
|
+
}
|
|
15199
|
+
return { clientId: newId, created: true };
|
|
15200
|
+
} catch (error) {
|
|
15201
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
15202
|
+
print(` ${c.yellow}Azure CLI failed: ${msg}${c.reset}`);
|
|
14969
15203
|
}
|
|
14970
|
-
|
|
14971
|
-
}
|
|
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 };
|
|
15204
|
+
} else {
|
|
15205
|
+
print(` ${c.dim}Azure CLI not found \u2014 skipping automatic app creation${c.reset}`);
|
|
14977
15206
|
}
|
|
15207
|
+
const manualId = await promptForClientId(askUser);
|
|
15208
|
+
print(` ${icons.check} Client ID: ${c.dim}${manualId}${c.reset}`);
|
|
15209
|
+
return { clientId: manualId, created: false };
|
|
14978
15210
|
}
|
|
14979
15211
|
async function runSetupWizard() {
|
|
14980
15212
|
const rl = createInterface({
|
|
@@ -14992,7 +15224,7 @@ async function runSetupWizard() {
|
|
|
14992
15224
|
printHeader();
|
|
14993
15225
|
const totalSteps = 5;
|
|
14994
15226
|
printStep(1, totalSteps, "App Registration");
|
|
14995
|
-
const appResult = await resolveAppRegistration();
|
|
15227
|
+
const appResult = await resolveAppRegistration(prompt);
|
|
14996
15228
|
const clientId = appResult.clientId;
|
|
14997
15229
|
printStep(2, totalSteps, "Sign In");
|
|
14998
15230
|
const cacheResult = clearTokenCache();
|
|
@@ -15014,6 +15246,18 @@ async function runSetupWizard() {
|
|
|
15014
15246
|
} else {
|
|
15015
15247
|
print(` ${icons.check} ${c.green}Signed in${c.reset}`);
|
|
15016
15248
|
}
|
|
15249
|
+
const additionalResources = [
|
|
15250
|
+
"https://api.bap.microsoft.com",
|
|
15251
|
+
"https://service.powerapps.com"
|
|
15252
|
+
];
|
|
15253
|
+
for (const resource of additionalResources) {
|
|
15254
|
+
try {
|
|
15255
|
+
await authProvider.getTokenForResource(resource);
|
|
15256
|
+
print(` ${icons.check} ${c.dim}Authorized ${resource.replace("https://", "")}${c.reset}`);
|
|
15257
|
+
} catch {
|
|
15258
|
+
print(` ${c.dim}${icons.warn} Could not authorize ${resource.replace("https://", "")} (admin tools may be limited)${c.reset}`);
|
|
15259
|
+
}
|
|
15260
|
+
}
|
|
15017
15261
|
} catch (error) {
|
|
15018
15262
|
const message = error instanceof Error ? error.message : String(error);
|
|
15019
15263
|
print(` ${icons.cross} ${c.red}Sign-in failed:${c.reset} ${message}`);
|
|
@@ -15288,10 +15532,17 @@ Options:
|
|
|
15288
15532
|
--debug Enable debug-level logging
|
|
15289
15533
|
--help, -h Show this help message
|
|
15290
15534
|
|
|
15535
|
+
Environment Variables:
|
|
15536
|
+
PA_MCP_CLIENT_ID Microsoft Entra app client ID (overrides config file)
|
|
15537
|
+
PA_CONFIG_PATH Custom path to config.json
|
|
15538
|
+
|
|
15291
15539
|
First Run:
|
|
15292
15540
|
If no configuration exists, the setup wizard will run automatically.
|
|
15293
15541
|
You can also run it manually with --setup.
|
|
15294
15542
|
|
|
15543
|
+
Each tenant needs its own Microsoft Entra app registration.
|
|
15544
|
+
The wizard can create one via Azure CLI, or you can provide one manually.
|
|
15545
|
+
|
|
15295
15546
|
Configuration:
|
|
15296
15547
|
Config file: ${configPath}
|
|
15297
15548
|
|