powerautomate-mcp 0.7.2 → 0.7.5
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/README.md +8 -5
- package/dist/index.js +78 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -20,13 +20,16 @@ An MCP (Model Context Protocol) server that connects Claude to Microsoft Power A
|
|
|
20
20
|
- Microsoft 365 work account with Power Automate access
|
|
21
21
|
- **Linux only**: libsecret for secure token storage
|
|
22
22
|
```bash
|
|
23
|
-
# Ubuntu/Debian
|
|
24
|
-
sudo apt-get install libsecret-1-
|
|
23
|
+
# Ubuntu/Debian runtime
|
|
24
|
+
sudo apt-get install libsecret-1-0 gnome-keyring
|
|
25
25
|
|
|
26
|
-
# Fedora/RHEL
|
|
27
|
-
sudo dnf install libsecret
|
|
26
|
+
# Fedora/RHEL runtime
|
|
27
|
+
sudo dnf install libsecret gnome-keyring
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
If setup fails with `libsecret-1.so.0: cannot open shared object file`, the
|
|
31
|
+
runtime package above is missing.
|
|
32
|
+
|
|
30
33
|
### Installation
|
|
31
34
|
|
|
32
35
|
```bash
|
|
@@ -215,7 +218,7 @@ npm run build
|
|
|
215
218
|
|
|
216
219
|
This server implements defense-in-depth security:
|
|
217
220
|
|
|
218
|
-
- **Secure Token Storage**: DPAPI (Windows), Keychain (macOS), libsecret
|
|
221
|
+
- **Secure Token Storage**: DPAPI (Windows), Keychain (macOS), libsecret on Linux when available, with a 0o600 file-cache fallback when it is not
|
|
219
222
|
- **Input Validation**: GUID validation on all IDs, OData injection protection (ASCII-only), path traversal blocking (including URL-encoded), SharePoint hostname allowlist
|
|
220
223
|
- **SSRF Prevention**: Domain allowlists on Dataverse URLs, resource links, and token resources
|
|
221
224
|
- **Injection Prevention**: Power Automate expression injection blocking (`@{`/`}@`), OData filter sanitization, command injection prevention (`execFile` over `exec`)
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,6 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
|
|
|
13
13
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourceTemplatesRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
import { PublicClientApplication, LogLevel } from '@azure/msal-node';
|
|
15
15
|
import { execFile } from 'child_process';
|
|
16
|
-
import { PersistenceCreator, DataProtectionScope, PersistenceCachePlugin } from '@azure/msal-node-extensions';
|
|
17
16
|
import { request } from 'undici';
|
|
18
17
|
import Database from 'better-sqlite3';
|
|
19
18
|
import { fileURLToPath } from 'url';
|
|
@@ -725,6 +724,23 @@ function validateEnvironmentId(envId) {
|
|
|
725
724
|
}
|
|
726
725
|
return envId;
|
|
727
726
|
}
|
|
727
|
+
var msalNodeExtensionsPromise = null;
|
|
728
|
+
async function loadMsalNodeExtensions() {
|
|
729
|
+
if (!msalNodeExtensionsPromise) {
|
|
730
|
+
msalNodeExtensionsPromise = import('@azure/msal-node-extensions').then((mod) => {
|
|
731
|
+
const moduleWithOptionalDefault = mod;
|
|
732
|
+
const resolved = moduleWithOptionalDefault.default ?? moduleWithOptionalDefault;
|
|
733
|
+
if (!resolved || !("PersistenceCreator" in resolved)) {
|
|
734
|
+
throw new Error("msal-node-extensions did not expose the expected exports");
|
|
735
|
+
}
|
|
736
|
+
return resolved;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
return msalNodeExtensionsPromise;
|
|
740
|
+
}
|
|
741
|
+
function formatError(error) {
|
|
742
|
+
return error instanceof Error ? error.message : String(error);
|
|
743
|
+
}
|
|
728
744
|
var NativeFileCachePlugin = class {
|
|
729
745
|
cachePath;
|
|
730
746
|
constructor(cachePath) {
|
|
@@ -765,6 +781,11 @@ async function createPersistentCachePlugin(cachePath) {
|
|
|
765
781
|
logger.debug({ cacheLocation, platform: platform }, "Creating persistent token cache");
|
|
766
782
|
if (platform === "win32" || platform === "darwin") {
|
|
767
783
|
try {
|
|
784
|
+
const {
|
|
785
|
+
DataProtectionScope,
|
|
786
|
+
PersistenceCreator,
|
|
787
|
+
PersistenceCachePlugin
|
|
788
|
+
} = await loadMsalNodeExtensions();
|
|
768
789
|
const persistence = await PersistenceCreator.createPersistence({
|
|
769
790
|
cachePath: cacheLocation,
|
|
770
791
|
dataProtectionScope: platform === "win32" ? DataProtectionScope.CurrentUser : void 0,
|
|
@@ -777,11 +798,15 @@ async function createPersistentCachePlugin(cachePath) {
|
|
|
777
798
|
} catch (err) {
|
|
778
799
|
logger.error({ err, cacheLocation }, "Failed to create persistent cache");
|
|
779
800
|
throw new Error(
|
|
780
|
-
`Failed to initialize token cache at ${cacheLocation}: ${
|
|
801
|
+
`Failed to initialize token cache at ${cacheLocation}: ${formatError(err)}`
|
|
781
802
|
);
|
|
782
803
|
}
|
|
783
804
|
}
|
|
784
805
|
try {
|
|
806
|
+
const {
|
|
807
|
+
PersistenceCreator,
|
|
808
|
+
PersistenceCachePlugin
|
|
809
|
+
} = await loadMsalNodeExtensions();
|
|
785
810
|
const persistence = await PersistenceCreator.createPersistence({
|
|
786
811
|
cachePath: cacheLocation,
|
|
787
812
|
serviceName: "powerautomate-mcp",
|
|
@@ -793,7 +818,7 @@ async function createPersistentCachePlugin(cachePath) {
|
|
|
793
818
|
return new PersistenceCachePlugin(persistence);
|
|
794
819
|
} catch (libsecretError) {
|
|
795
820
|
logger.warn(
|
|
796
|
-
{ err:
|
|
821
|
+
{ err: formatError(libsecretError) },
|
|
797
822
|
"libsecret unavailable \u2014 using native file cache fallback (0o600 permissions)"
|
|
798
823
|
);
|
|
799
824
|
logger.info({ cacheLocation }, "Persistent token cache initialized (native file)");
|
|
@@ -1041,6 +1066,19 @@ async function createWamAuthProvider(config, silentOnly = false) {
|
|
|
1041
1066
|
const result = await acquireToken(SCOPES.FLOW);
|
|
1042
1067
|
return result.accessToken;
|
|
1043
1068
|
},
|
|
1069
|
+
async tryGetTokenForResourceSilent(resource) {
|
|
1070
|
+
try {
|
|
1071
|
+
validateHttpsUrl(resource, "Token resource");
|
|
1072
|
+
const url = new URL(resource);
|
|
1073
|
+
if (!isAllowedTokenDomain(url.hostname)) return null;
|
|
1074
|
+
if (!currentAccount) return null;
|
|
1075
|
+
const scopes = [`${resource}/.default`];
|
|
1076
|
+
const result = await pca.acquireTokenSilent({ scopes, account: currentAccount });
|
|
1077
|
+
return result.accessToken;
|
|
1078
|
+
} catch {
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
},
|
|
1044
1082
|
async getTokenForResource(resource) {
|
|
1045
1083
|
validateHttpsUrl(resource, "Token resource");
|
|
1046
1084
|
const url = new URL(resource);
|
|
@@ -14857,6 +14895,26 @@ async function createDeviceCodeAuthProvider(config, silentOnly = false) {
|
|
|
14857
14895
|
const result = await acquireToken(scopes);
|
|
14858
14896
|
return result.accessToken;
|
|
14859
14897
|
},
|
|
14898
|
+
async tryGetTokenForResourceSilent(resource) {
|
|
14899
|
+
try {
|
|
14900
|
+
validateHttpsUrl(resource, "Token resource");
|
|
14901
|
+
const url = new URL(resource);
|
|
14902
|
+
if (!isAllowedTokenDomain(url.hostname)) {
|
|
14903
|
+
return null;
|
|
14904
|
+
}
|
|
14905
|
+
if (!currentAccount) {
|
|
14906
|
+
return null;
|
|
14907
|
+
}
|
|
14908
|
+
const scopes = [`${resource}/.default`];
|
|
14909
|
+
const result = await pca.acquireTokenSilent({
|
|
14910
|
+
scopes,
|
|
14911
|
+
account: currentAccount
|
|
14912
|
+
});
|
|
14913
|
+
return result.accessToken;
|
|
14914
|
+
} catch {
|
|
14915
|
+
return null;
|
|
14916
|
+
}
|
|
14917
|
+
},
|
|
14860
14918
|
async logout() {
|
|
14861
14919
|
if (currentAccount) {
|
|
14862
14920
|
const cache = pca.getTokenCache();
|
|
@@ -14952,18 +15010,21 @@ var REQUIRED_RESOURCE_ACCESS = [
|
|
|
14952
15010
|
]
|
|
14953
15011
|
},
|
|
14954
15012
|
{
|
|
14955
|
-
resourceAppId: "
|
|
15013
|
+
resourceAppId: "475226c6-020e-4fb2-8a90-7a972cbfc1d4",
|
|
14956
15014
|
resourceAccess: [
|
|
14957
|
-
{ id: "
|
|
15015
|
+
{ id: "a4c50e78-6978-4ce3-b1c0-965c1f9b1128", type: "Scope" }
|
|
14958
15016
|
]
|
|
14959
15017
|
},
|
|
14960
15018
|
{
|
|
14961
|
-
// Business Application Platform (BAP) API — required for environment
|
|
14962
|
-
// discovery and Power Platform admin operations
|
|
14963
15019
|
resourceAppId: "0e0bf3cc-3078-4fd4-9ef3-cb6dc0245b10",
|
|
14964
15020
|
resourceAccess: [
|
|
14965
15021
|
{ id: "4ae1bf56-f562-4747-b7bc-2fa0874ed46f", type: "Scope" }
|
|
14966
|
-
|
|
15022
|
+
]
|
|
15023
|
+
},
|
|
15024
|
+
{
|
|
15025
|
+
resourceAppId: "00000007-0000-0000-c000-000000000000",
|
|
15026
|
+
resourceAccess: [
|
|
15027
|
+
{ id: "78ce3f0f-a1ce-49c2-8cde-64b5c0896db4", type: "Scope" }
|
|
14967
15028
|
]
|
|
14968
15029
|
}
|
|
14969
15030
|
];
|
|
@@ -15098,6 +15159,7 @@ var c = {
|
|
|
15098
15159
|
var icons = {
|
|
15099
15160
|
check: `${c.green}\u2713${c.reset}`,
|
|
15100
15161
|
cross: `${c.red}\u2717${c.reset}`,
|
|
15162
|
+
info: `${c.blue}i${c.reset}`,
|
|
15101
15163
|
arrow: `${c.cyan}\u2192${c.reset}`,
|
|
15102
15164
|
bullet: `${c.dim}\u2022${c.reset}`,
|
|
15103
15165
|
star: `${c.yellow}\u2605${c.reset}`
|
|
@@ -15266,15 +15328,15 @@ async function runSetupWizard() {
|
|
|
15266
15328
|
print(` ${icons.check} ${c.green}Signed in${c.reset}`);
|
|
15267
15329
|
}
|
|
15268
15330
|
const additionalResources = [
|
|
15269
|
-
"https://
|
|
15270
|
-
"https://
|
|
15331
|
+
{ url: "https://service.powerapps.com", label: "PowerApps (connections)" },
|
|
15332
|
+
{ url: "https://api.bap.microsoft.com", label: "Admin API (environments)" }
|
|
15271
15333
|
];
|
|
15272
|
-
for (const
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
print(` ${icons.check} ${c.dim}
|
|
15276
|
-
}
|
|
15277
|
-
print(` ${c.dim}${icons.
|
|
15334
|
+
for (const { url, label } of additionalResources) {
|
|
15335
|
+
const token = authProvider.tryGetTokenForResourceSilent ? await authProvider.tryGetTokenForResourceSilent(url) : null;
|
|
15336
|
+
if (token) {
|
|
15337
|
+
print(` ${icons.check} ${c.dim}${label}: authorized${c.reset}`);
|
|
15338
|
+
} else {
|
|
15339
|
+
print(` ${c.dim}${icons.info || "-"} ${label}: will be authorized via admin consent (Step 3)${c.reset}`);
|
|
15278
15340
|
}
|
|
15279
15341
|
}
|
|
15280
15342
|
} catch (error) {
|