@softeria/ms-365-mcp-server 0.128.0 → 0.128.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/dist/auth.js +43 -18
- package/dist/endpoints.json +1 -0
- package/dist/graph-client.js +2 -0
- package/dist/graph-tools.js +15 -5
- package/dist/lib/tool-schema.js +2 -2
- package/package.json +1 -1
- package/src/endpoints.json +1 -0
package/dist/auth.js
CHANGED
|
@@ -31,6 +31,36 @@ function createMsalConfig(secrets) {
|
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
function buildDiskCoherencyCachePlugin(storage) {
|
|
35
|
+
return {
|
|
36
|
+
beforeCacheAccess: async (context) => {
|
|
37
|
+
try {
|
|
38
|
+
const cacheRaw = await storage.load("token-cache");
|
|
39
|
+
if (cacheRaw) {
|
|
40
|
+
context.tokenCache.deserialize(unwrapCache(cacheRaw).data);
|
|
41
|
+
}
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger.error(`Error reloading token cache: ${error.message}`);
|
|
44
|
+
if (storage.failClosed) {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
afterCacheAccess: async (context) => {
|
|
50
|
+
if (!context.cacheHasChanged) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
await storage.save("token-cache", wrapCache(context.tokenCache.serialize()));
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logger.error(`Error saving token cache: ${error.message}`);
|
|
57
|
+
if (storage.failClosed) {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
34
64
|
const SCOPE_HIERARCHY = {
|
|
35
65
|
"Mail.ReadWrite": ["Mail.Read"],
|
|
36
66
|
"Calendars.ReadWrite": ["Calendars.Read"],
|
|
@@ -312,8 +342,15 @@ function consumersAuthorityHint(error, account, authority) {
|
|
|
312
342
|
class AuthManager {
|
|
313
343
|
constructor(config, scopes = [], expectedAccount, storage) {
|
|
314
344
|
logger.info(`And scopes are ${scopes.join(", ")}`, scopes);
|
|
315
|
-
this.config = config;
|
|
316
345
|
this.scopes = scopes;
|
|
346
|
+
this.storage = storage ?? new DefaultTokenCacheStorage();
|
|
347
|
+
this.config = {
|
|
348
|
+
...config,
|
|
349
|
+
cache: {
|
|
350
|
+
...config.cache,
|
|
351
|
+
cachePlugin: buildDiskCoherencyCachePlugin(this.storage)
|
|
352
|
+
}
|
|
353
|
+
};
|
|
317
354
|
this.msalApp = new PublicClientApplication(this.config);
|
|
318
355
|
this.accessToken = null;
|
|
319
356
|
this.tokenExpiry = null;
|
|
@@ -323,7 +360,6 @@ class AuthManager {
|
|
|
323
360
|
this.expectedHomeAccountId = this.normalizeExpectedHomeAccountId(
|
|
324
361
|
expectedAccount?.expectedHomeAccountId
|
|
325
362
|
);
|
|
326
|
-
this.storage = storage ?? new DefaultTokenCacheStorage();
|
|
327
363
|
const oauthTokenFromEnv = process.env.MS365_MCP_OAUTH_TOKEN;
|
|
328
364
|
this.oauthToken = oauthTokenFromEnv ?? null;
|
|
329
365
|
this.isOAuthMode = oauthTokenFromEnv != null;
|
|
@@ -367,17 +403,6 @@ class AuthManager {
|
|
|
367
403
|
}
|
|
368
404
|
}
|
|
369
405
|
}
|
|
370
|
-
async saveTokenCache() {
|
|
371
|
-
try {
|
|
372
|
-
const stamped = wrapCache(this.msalApp.getTokenCache().serialize());
|
|
373
|
-
await this.storage.save("token-cache", stamped);
|
|
374
|
-
} catch (error) {
|
|
375
|
-
logger.error(`Error saving token cache: ${error.message}`);
|
|
376
|
-
if (this.storage.failClosed) {
|
|
377
|
-
throw error;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
406
|
async saveSelectedAccount() {
|
|
382
407
|
try {
|
|
383
408
|
const stamped = wrapCache(JSON.stringify({ accountId: this.selectedAccountId }));
|
|
@@ -491,7 +516,10 @@ class AuthManager {
|
|
|
491
516
|
try {
|
|
492
517
|
await this.msalApp.getTokenCache().removeAccount(account);
|
|
493
518
|
} catch (error) {
|
|
494
|
-
logger.
|
|
519
|
+
logger.error(`Failed to remove unexpected account from cache: ${error.message}`);
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Authenticated Microsoft account '${this.describeAccount(account)}' does not match expected Microsoft account '${this.expectedAccountLabel()}', and it could not be removed from the token cache (${error.message}). Its tokens may remain persisted - run --logout to clear the cache, then re-login.`
|
|
522
|
+
);
|
|
495
523
|
}
|
|
496
524
|
throw new Error(
|
|
497
525
|
`Authenticated Microsoft account '${this.describeAccount(account)}' does not match expected Microsoft account '${this.expectedAccountLabel()}'. Login was not persisted.`
|
|
@@ -522,7 +550,6 @@ class AuthManager {
|
|
|
522
550
|
const response = await this.msalApp.acquireTokenSilent(silentRequest);
|
|
523
551
|
this.accessToken = response.accessToken;
|
|
524
552
|
this.tokenExpiry = response.expiresOn ? new Date(response.expiresOn).getTime() : null;
|
|
525
|
-
await this.saveTokenCache();
|
|
526
553
|
return this.accessToken;
|
|
527
554
|
} catch (error) {
|
|
528
555
|
const hint = consumersAuthorityHint(error, currentAccount, this.config.auth.authority);
|
|
@@ -584,7 +611,6 @@ class AuthManager {
|
|
|
584
611
|
await this.saveSelectedAccount();
|
|
585
612
|
logger.info(`Auto-selected new account: ${response.account.username}`);
|
|
586
613
|
}
|
|
587
|
-
await this.saveTokenCache();
|
|
588
614
|
return this.accessToken;
|
|
589
615
|
} catch (error) {
|
|
590
616
|
logger.error(`Error in device code flow: ${error.message}`);
|
|
@@ -626,7 +652,6 @@ class AuthManager {
|
|
|
626
652
|
await this.saveSelectedAccount();
|
|
627
653
|
logger.info(`Auto-selected new account: ${response.account.username}`);
|
|
628
654
|
}
|
|
629
|
-
await this.saveTokenCache();
|
|
630
655
|
return this.accessToken;
|
|
631
656
|
} catch (error) {
|
|
632
657
|
logger.error(`Error in interactive browser flow: ${error.message}`);
|
|
@@ -842,7 +867,6 @@ class AuthManager {
|
|
|
842
867
|
};
|
|
843
868
|
try {
|
|
844
869
|
const response = await this.msalApp.acquireTokenSilent(silentRequest);
|
|
845
|
-
await this.saveTokenCache();
|
|
846
870
|
return response.accessToken;
|
|
847
871
|
} catch (error) {
|
|
848
872
|
const hint = consumersAuthorityHint(error, targetAccount, this.config.auth.authority);
|
|
@@ -858,6 +882,7 @@ class AuthManager {
|
|
|
858
882
|
var auth_default = AuthManager;
|
|
859
883
|
export {
|
|
860
884
|
buildAllowedScopeDiagnostics,
|
|
885
|
+
buildDiskCoherencyCachePlugin,
|
|
861
886
|
buildScopeDiagnostics,
|
|
862
887
|
buildScopesFromEndpoints,
|
|
863
888
|
collapseScopeHierarchy,
|
package/dist/endpoints.json
CHANGED
|
@@ -359,6 +359,7 @@
|
|
|
359
359
|
"toolName": "create-calendar-event",
|
|
360
360
|
"presets": ["calendar", "outlook", "personal"],
|
|
361
361
|
"scopes": ["Calendars.ReadWrite"],
|
|
362
|
+
"descriptionOverride": "Create (schedule) a new calendar event — a meeting or appointment — on the user's calendar. Set subject, start/end times, time zone, location, body, and attendees; supports online meetings and recurrence.",
|
|
362
363
|
"llmTip": "CRITICAL: Do not try to guess the email address of the recipients. Use the list-users tool to find the email address of the recipients."
|
|
363
364
|
},
|
|
364
365
|
{
|
package/dist/graph-client.js
CHANGED
package/dist/graph-tools.js
CHANGED
|
@@ -170,7 +170,10 @@ const UTILITY_TOOLS = [
|
|
|
170
170
|
if (authManager && !authManager.isOAuthModeEnabled() && !getRequestTokens()) {
|
|
171
171
|
accountAccessToken = await authManager.getTokenForAccount(accountParam);
|
|
172
172
|
}
|
|
173
|
-
return await graphClient.graphRequest(target, {
|
|
173
|
+
return await graphClient.graphRequest(target, {
|
|
174
|
+
accessToken: accountAccessToken,
|
|
175
|
+
rawResponse: true
|
|
176
|
+
});
|
|
174
177
|
} catch (error) {
|
|
175
178
|
return {
|
|
176
179
|
content: [{ type: "text", text: JSON.stringify({ error: error.message }) }],
|
|
@@ -823,7 +826,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
823
826
|
).optional();
|
|
824
827
|
}
|
|
825
828
|
let toolDescription = withApiVersionPrefix(
|
|
826
|
-
tool.description || `Execute ${tool.method.toUpperCase()} request to ${tool.path}`,
|
|
829
|
+
(endpointConfig?.descriptionOverride ?? tool.description) || `Execute ${tool.method.toUpperCase()} request to ${tool.path}`,
|
|
827
830
|
endpointConfig
|
|
828
831
|
);
|
|
829
832
|
if (endpointConfig?.llmTip) {
|
|
@@ -920,7 +923,10 @@ function buildDiscoverySearchIndex(toolsRegistry, utilityTools = []) {
|
|
|
920
923
|
const nt = tokenize(name);
|
|
921
924
|
nameTokens.set(name, new Set(nt));
|
|
922
925
|
const pathTokens = tokenize(tool.path);
|
|
923
|
-
const descTokens = tokenize(tool.description).slice(
|
|
926
|
+
const descTokens = tokenize(config?.descriptionOverride ?? tool.description).slice(
|
|
927
|
+
0,
|
|
928
|
+
DESC_CAP_TOKENS
|
|
929
|
+
);
|
|
924
930
|
const tipTokens = tokenize(config?.llmTip).slice(0, TIP_EXCERPT_TOKENS);
|
|
925
931
|
const tokens = [
|
|
926
932
|
...nt,
|
|
@@ -1032,7 +1038,7 @@ function registerDiscoveryTools(server, graphClient, readOnly = false, orgMode =
|
|
|
1032
1038
|
method: tool.method.toUpperCase(),
|
|
1033
1039
|
path: tool.path,
|
|
1034
1040
|
description: withApiVersionPrefix(
|
|
1035
|
-
tool.description || `${tool.method.toUpperCase()} ${tool.path}`,
|
|
1041
|
+
(config?.descriptionOverride ?? tool.description) || `${tool.method.toUpperCase()} ${tool.path}`,
|
|
1036
1042
|
config
|
|
1037
1043
|
),
|
|
1038
1044
|
...config?.llmTip ? { llmTip: config.llmTip } : {}
|
|
@@ -1111,7 +1117,11 @@ function registerDiscoveryTools(server, graphClient, readOnly = false, orgMode =
|
|
|
1111
1117
|
async ({ tool_name }) => {
|
|
1112
1118
|
const entry = toolsRegistry.get(tool_name);
|
|
1113
1119
|
if (entry) {
|
|
1114
|
-
const schema = describeToolSchema(
|
|
1120
|
+
const schema = describeToolSchema(
|
|
1121
|
+
entry.tool,
|
|
1122
|
+
entry.config?.llmTip,
|
|
1123
|
+
entry.config?.descriptionOverride
|
|
1124
|
+
);
|
|
1115
1125
|
return {
|
|
1116
1126
|
content: [{ type: "text", text: JSON.stringify(schema, null, 2) }]
|
|
1117
1127
|
};
|
package/dist/lib/tool-schema.js
CHANGED
|
@@ -7,7 +7,7 @@ function unwrapOptional(schema) {
|
|
|
7
7
|
}
|
|
8
8
|
return { inner: schema, optional: false };
|
|
9
9
|
}
|
|
10
|
-
function describeToolSchema(tool, llmTip) {
|
|
10
|
+
function describeToolSchema(tool, llmTip, descriptionOverride) {
|
|
11
11
|
const params = (tool.parameters ?? []).map((p) => {
|
|
12
12
|
const { inner, optional } = unwrapOptional(p.schema);
|
|
13
13
|
const isPath = p.type === "Path";
|
|
@@ -25,7 +25,7 @@ function describeToolSchema(tool, llmTip) {
|
|
|
25
25
|
name: tool.alias,
|
|
26
26
|
method: tool.method.toUpperCase(),
|
|
27
27
|
path: tool.path,
|
|
28
|
-
description: tool.description ?? "",
|
|
28
|
+
description: descriptionOverride ?? tool.description ?? "",
|
|
29
29
|
...llmTip ? { llmTip } : {},
|
|
30
30
|
parameters: params
|
|
31
31
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.128.
|
|
3
|
+
"version": "0.128.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",
|
package/src/endpoints.json
CHANGED
|
@@ -359,6 +359,7 @@
|
|
|
359
359
|
"toolName": "create-calendar-event",
|
|
360
360
|
"presets": ["calendar", "outlook", "personal"],
|
|
361
361
|
"scopes": ["Calendars.ReadWrite"],
|
|
362
|
+
"descriptionOverride": "Create (schedule) a new calendar event — a meeting or appointment — on the user's calendar. Set subject, start/end times, time zone, location, body, and attendees; supports online meetings and recurrence.",
|
|
362
363
|
"llmTip": "CRITICAL: Do not try to guess the email address of the recipients. Use the list-users tool to find the email address of the recipients."
|
|
363
364
|
},
|
|
364
365
|
{
|