@softeria/ms-365-mcp-server 0.118.0 → 0.118.2

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 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
- logger.error(`Silent token acquisition failed: ${describeAuthError(error)}`);
463
- throw new Error("Silent token acquisition failed");
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
- logger.error(`Silent token acquisition failed: ${describeAuthError(error)}`);
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,
@@ -10,9 +10,17 @@ const REDACTIONS = [
10
10
  replacement: "$1[REDACTED]"
11
11
  },
12
12
  // OAuth token fields in query strings or JSON bodies:
13
- // refresh_token=..., "access_token": "...", code=..., client_secret=...
13
+ // refresh_token=..., "access_token": "...", client_secret=...
14
+ // The \b keeps composite keys like `statusCode` from matching via a substring.
14
15
  {
15
- pattern: /(["']?(?:refresh_token|access_token|id_token|client_secret|code|assertion)["']?\s*[=:]\s*["']?)[A-Za-z0-9._~+/-]+=*/gi,
16
+ pattern: /(["']?\b(?:refresh_token|access_token|id_token|client_secret|assertion)["']?\s*[=:]\s*["']?)[A-Za-z0-9._~+/-]+=*/gi,
17
+ replacement: "$1[REDACTED]"
18
+ },
19
+ // OAuth authorization codes, query-string form only (?code=... / &code=...).
20
+ // `code:` in JSON or prose is an error/status code (ECONNRESET, AADSTS...),
21
+ // which must stay readable for diagnostics.
22
+ {
23
+ pattern: /([?&]code=)[^&\s"']+/gi,
16
24
  replacement: "$1[REDACTED]"
17
25
  },
18
26
  // Email addresses / UPNs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.118.0",
3
+ "version": "0.118.2",
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",