@softeria/ms-365-mcp-server 0.118.1 → 0.119.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/README.md +1 -1
- package/dist/auth.js +20 -4
- package/dist/cli.js +6 -0
- package/dist/lib/microsoft-auth.js +20 -0
- package/dist/server.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -584,7 +584,7 @@ Environment variables:
|
|
|
584
584
|
- `SILENT=true|1`: Disable console output
|
|
585
585
|
- `MS365_MCP_REDACT_PII=true|1`: Scrub JWTs, Bearer headers, OAuth token fields, and email addresses from log messages before they are written (default: disabled). Useful when logs are shipped to a central store or shared host.
|
|
586
586
|
- `MS365_MCP_CLIENT_ID`: Custom Azure app client ID (defaults to built-in app)
|
|
587
|
-
- `MS365_MCP_TENANT_ID`: Custom tenant ID (defaults to 'common' for multi-tenant)
|
|
587
|
+
- `MS365_MCP_TENANT_ID`: Custom tenant ID (defaults to 'common' for multi-tenant). **Personal Microsoft accounts should set this to `consumers`** - as of June 2026, refresh tokens issued via the default 'common' authority are rejected at the first refresh, so sessions die roughly an hour after login
|
|
588
588
|
- `MS365_MCP_OAUTH_TOKEN`: Pre-existing OAuth token for Microsoft Graph API (BYOT method)
|
|
589
589
|
- `MS365_MCP_KEYVAULT_URL`: Azure Key Vault URL for secrets management (see Azure Key Vault section)
|
|
590
590
|
- `MS365_MCP_TOKEN_CACHE_PATH`: Custom file path for MSAL token cache (see Token Storage below)
|
package/dist/auth.js
CHANGED
|
@@ -243,6 +243,13 @@ function describeAuthError(error) {
|
|
|
243
243
|
}
|
|
244
244
|
return error.message;
|
|
245
245
|
}
|
|
246
|
+
const MSA_HOME_TENANT_ID = "9188040d-6c67-4c5b-b112-36a304b66dad";
|
|
247
|
+
function consumersAuthorityHint(error, account, authority) {
|
|
248
|
+
if (error instanceof AuthError && error.errorCode === "invalid_grant" && account?.tenantId === MSA_HOME_TENANT_ID && (!authority || /\/common\/?$/i.test(authority))) {
|
|
249
|
+
return `This looks like a known issue (June 2026) where Microsoft rejects refresh tokens issued to personal accounts via the default 'common' authority. If this server is used only with personal accounts, set MS365_MCP_TENANT_ID=consumers and re-login with: --login`;
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
246
253
|
class AuthManager {
|
|
247
254
|
constructor(config, scopes = [], expectedAccount, storage) {
|
|
248
255
|
logger.info(`And scopes are ${scopes.join(", ")}`, scopes);
|
|
@@ -459,8 +466,13 @@ class AuthManager {
|
|
|
459
466
|
await this.saveTokenCache();
|
|
460
467
|
return this.accessToken;
|
|
461
468
|
} catch (error) {
|
|
462
|
-
|
|
463
|
-
|
|
469
|
+
const hint = consumersAuthorityHint(error, currentAccount, this.config.auth.authority);
|
|
470
|
+
logger.error(
|
|
471
|
+
`Silent token acquisition failed: ${describeAuthError(error)}${hint ? ` ${hint}` : ""}`
|
|
472
|
+
);
|
|
473
|
+
throw new Error(
|
|
474
|
+
hint ? `Silent token acquisition failed. ${hint}` : "Silent token acquisition failed"
|
|
475
|
+
);
|
|
464
476
|
}
|
|
465
477
|
}
|
|
466
478
|
throw new Error("No valid token found");
|
|
@@ -774,9 +786,12 @@ class AuthManager {
|
|
|
774
786
|
await this.saveTokenCache();
|
|
775
787
|
return response.accessToken;
|
|
776
788
|
} catch (error) {
|
|
777
|
-
|
|
789
|
+
const hint = consumersAuthorityHint(error, targetAccount, this.config.auth.authority);
|
|
790
|
+
logger.error(
|
|
791
|
+
`Silent token acquisition failed: ${describeAuthError(error)}${hint ? ` ${hint}` : ""}`
|
|
792
|
+
);
|
|
778
793
|
throw new Error(
|
|
779
|
-
`Failed to acquire token for account '${targetAccount.username || targetAccount.name || "unknown"}'. The token may have expired. Please re-login with: --login
|
|
794
|
+
`Failed to acquire token for account '${targetAccount.username || targetAccount.name || "unknown"}'. ` + (hint ?? "The token may have expired. Please re-login with: --login")
|
|
780
795
|
);
|
|
781
796
|
}
|
|
782
797
|
}
|
|
@@ -787,6 +802,7 @@ export {
|
|
|
787
802
|
buildScopeDiagnostics,
|
|
788
803
|
buildScopesFromEndpoints,
|
|
789
804
|
collapseScopeHierarchy,
|
|
805
|
+
consumersAuthorityHint,
|
|
790
806
|
auth_default as default,
|
|
791
807
|
describeAuthError,
|
|
792
808
|
getEndpointRequiredScopes,
|
package/dist/cli.js
CHANGED
|
@@ -50,6 +50,9 @@ program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").versio
|
|
|
50
50
|
).option(
|
|
51
51
|
"--trust-proxy-auth",
|
|
52
52
|
"In HTTP mode, skip the built-in Bearer-token check on /mcp and ignore any forwarded Authorization header. All callers share the locally cached MSAL identity (same path stdio mode uses). Use only when an upstream reverse proxy has already authenticated the caller."
|
|
53
|
+
).option(
|
|
54
|
+
"--allow-unauthenticated-discovery",
|
|
55
|
+
"In HTTP mode, allow MCP discovery requests (initialize, tools/list, prompts/list, resources/list, ping) without a bearer token, so a gateway can enumerate the tool catalog before any user has authenticated. Non-discovery requests (e.g. tools/call) still require a token. Off by default."
|
|
53
56
|
).addOption(
|
|
54
57
|
// DEPRECATED: kept only so existing deployments that set --base-url or
|
|
55
58
|
// MS365_MCP_BASE_URL do not crash at startup. Use --public-url /
|
|
@@ -155,6 +158,9 @@ function parseArgs() {
|
|
|
155
158
|
if (process.env.MS365_MCP_TRUST_PROXY_AUTH === "true" || process.env.MS365_MCP_TRUST_PROXY_AUTH === "1") {
|
|
156
159
|
options.trustProxyAuth = true;
|
|
157
160
|
}
|
|
161
|
+
if (process.env.MS365_MCP_ALLOW_UNAUTHENTICATED_DISCOVERY === "true" || process.env.MS365_MCP_ALLOW_UNAUTHENTICATED_DISCOVERY === "1") {
|
|
162
|
+
options.allowUnauthenticatedDiscovery = true;
|
|
163
|
+
}
|
|
158
164
|
if (options.cloud) {
|
|
159
165
|
process.env.MS365_MCP_CLOUD_TYPE = options.cloud;
|
|
160
166
|
}
|
|
@@ -17,6 +17,22 @@ function isJwtExpired(token) {
|
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
const DISCOVERY_METHODS = /* @__PURE__ */ new Set([
|
|
21
|
+
"initialize",
|
|
22
|
+
"notifications/initialized",
|
|
23
|
+
"tools/list",
|
|
24
|
+
"prompts/list",
|
|
25
|
+
"resources/list",
|
|
26
|
+
"ping"
|
|
27
|
+
]);
|
|
28
|
+
function isDiscoveryRequest(req) {
|
|
29
|
+
if (req.method !== "POST" || !req.body) return false;
|
|
30
|
+
const body = req.body;
|
|
31
|
+
if (Array.isArray(body)) {
|
|
32
|
+
return body.every((item) => DISCOVERY_METHODS.has(item?.method));
|
|
33
|
+
}
|
|
34
|
+
return DISCOVERY_METHODS.has(body?.method);
|
|
35
|
+
}
|
|
20
36
|
const microsoftBearerTokenAuthMiddleware = (opts = {}) => (req, res, next) => {
|
|
21
37
|
if (opts.trustProxyAuth) {
|
|
22
38
|
next();
|
|
@@ -24,6 +40,10 @@ const microsoftBearerTokenAuthMiddleware = (opts = {}) => (req, res, next) => {
|
|
|
24
40
|
}
|
|
25
41
|
const authHeader = req.headers.authorization;
|
|
26
42
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
43
|
+
if (opts.allowUnauthenticatedDiscovery && isDiscoveryRequest(req)) {
|
|
44
|
+
next();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
27
47
|
res.status(401).set(
|
|
28
48
|
"WWW-Authenticate",
|
|
29
49
|
buildWwwAuthenticate(req, "invalid_token", "Missing or malformed Authorization header")
|
package/dist/server.js
CHANGED
|
@@ -469,7 +469,8 @@ class MicrosoftGraphServer {
|
|
|
469
469
|
})
|
|
470
470
|
);
|
|
471
471
|
const mcpAuth = microsoftBearerTokenAuthMiddleware({
|
|
472
|
-
trustProxyAuth: this.options.trustProxyAuth
|
|
472
|
+
trustProxyAuth: this.options.trustProxyAuth,
|
|
473
|
+
allowUnauthenticatedDiscovery: this.options.allowUnauthenticatedDiscovery
|
|
473
474
|
});
|
|
474
475
|
app.get(
|
|
475
476
|
"/mcp",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.119.0",
|
|
4
4
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|