@softeria/ms-365-mcp-server 0.9.0 → 0.9.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 +2 -0
- package/dist/auth.js +73 -1
- package/dist/cli.js +5 -1
- package/dist/endpoints.json +105 -0
- package/dist/generated/client.js +2771 -1975
- package/dist/graph-client.js +16 -4
- package/dist/graph-tools.js +15 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/src/endpoints.json +105 -0
package/README.md
CHANGED
|
@@ -132,11 +132,13 @@ When running as an MCP server, the following options can be used:
|
|
|
132
132
|
--http [port] Use Streamable HTTP transport instead of stdio (optionally specify port, default: 3000)
|
|
133
133
|
Starts Express.js server with MCP endpoint at /mcp
|
|
134
134
|
--enable-auth-tools Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)
|
|
135
|
+
--enabled-tools <pattern> Filter tools using regex pattern (e.g., "excel|contact" to enable Excel and Contact tools)
|
|
135
136
|
```
|
|
136
137
|
|
|
137
138
|
Environment variables:
|
|
138
139
|
|
|
139
140
|
- `READ_ONLY=true|1`: Alternative to --read-only flag
|
|
141
|
+
- `ENABLED_TOOLS`: Filter tools using regex pattern (alternative to --enabled-tools flag)
|
|
140
142
|
- `LOG_LEVEL`: Set logging level (default: 'info')
|
|
141
143
|
- `SILENT=true`: Disable console output
|
|
142
144
|
- `MS365_MCP_CLIENT_ID`: Custom Azure app client ID (defaults to built-in app)
|
package/dist/auth.js
CHANGED
|
@@ -24,9 +24,12 @@ const SCOPE_HIERARCHY = {
|
|
|
24
24
|
'Tasks.ReadWrite': ['Tasks.Read'],
|
|
25
25
|
'Contacts.ReadWrite': ['Contacts.Read'],
|
|
26
26
|
};
|
|
27
|
-
function buildScopesFromEndpoints() {
|
|
27
|
+
function buildScopesFromEndpoints(includeWorkAccountScopes = false) {
|
|
28
28
|
const scopesSet = new Set();
|
|
29
29
|
endpoints.default.forEach((endpoint) => {
|
|
30
|
+
if (endpoint.requiresWorkAccount && !includeWorkAccountScopes) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
30
33
|
if (endpoint.scopes && Array.isArray(endpoint.scopes)) {
|
|
31
34
|
endpoint.scopes.forEach((scope) => scopesSet.add(scope));
|
|
32
35
|
}
|
|
@@ -39,6 +42,9 @@ function buildScopesFromEndpoints() {
|
|
|
39
42
|
});
|
|
40
43
|
return Array.from(scopesSet);
|
|
41
44
|
}
|
|
45
|
+
function buildAllScopes() {
|
|
46
|
+
return buildScopesFromEndpoints(true);
|
|
47
|
+
}
|
|
42
48
|
class AuthManager {
|
|
43
49
|
constructor(config = DEFAULT_CONFIG, scopes = buildScopesFromEndpoints()) {
|
|
44
50
|
logger.info(`And scopes are ${scopes.join(', ')}`, scopes);
|
|
@@ -226,5 +232,71 @@ class AuthManager {
|
|
|
226
232
|
throw error;
|
|
227
233
|
}
|
|
228
234
|
}
|
|
235
|
+
async hasWorkAccountPermissions() {
|
|
236
|
+
try {
|
|
237
|
+
const accounts = await this.msalApp.getTokenCache().getAllAccounts();
|
|
238
|
+
if (accounts.length === 0) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
const workScopes = endpoints.default
|
|
242
|
+
.filter((e) => e.requiresWorkAccount)
|
|
243
|
+
.flatMap((e) => e.scopes || []);
|
|
244
|
+
try {
|
|
245
|
+
await this.msalApp.acquireTokenSilent({
|
|
246
|
+
scopes: workScopes.slice(0, 1),
|
|
247
|
+
account: accounts[0],
|
|
248
|
+
});
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
logger.error(`Error checking work account permissions: ${error.message}`);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async expandToWorkAccountScopes(hack) {
|
|
261
|
+
try {
|
|
262
|
+
logger.info('Expanding to work account scopes...');
|
|
263
|
+
const allScopes = buildAllScopes();
|
|
264
|
+
const deviceCodeRequest = {
|
|
265
|
+
scopes: allScopes,
|
|
266
|
+
deviceCodeCallback: (response) => {
|
|
267
|
+
const text = [
|
|
268
|
+
'\n',
|
|
269
|
+
'🔄 This feature requires additional permissions (work account scopes)',
|
|
270
|
+
'\n',
|
|
271
|
+
response.message,
|
|
272
|
+
'\n',
|
|
273
|
+
].join('');
|
|
274
|
+
if (hack) {
|
|
275
|
+
hack(text + 'After login run the "verify login" command');
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
console.log(text);
|
|
279
|
+
}
|
|
280
|
+
logger.info('Work account scope expansion initiated');
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
const response = await this.msalApp.acquireTokenByDeviceCode(deviceCodeRequest);
|
|
284
|
+
logger.info('Work account scope expansion successful');
|
|
285
|
+
this.accessToken = response?.accessToken || null;
|
|
286
|
+
this.tokenExpiry = response?.expiresOn ? new Date(response.expiresOn).getTime() : null;
|
|
287
|
+
this.scopes = allScopes;
|
|
288
|
+
await this.saveTokenCache();
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
logger.error(`Error expanding to work account scopes: ${error.message}`);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
requiresWorkAccountScope(toolName) {
|
|
297
|
+
const endpoint = endpoints.default.find((e) => e.toolName === toolName);
|
|
298
|
+
return endpoint?.requiresWorkAccount === true;
|
|
299
|
+
}
|
|
229
300
|
}
|
|
230
301
|
export default AuthManager;
|
|
302
|
+
export { buildScopesFromEndpoints, buildAllScopes };
|
package/dist/cli.js
CHANGED
|
@@ -17,12 +17,16 @@ program
|
|
|
17
17
|
.option('--verify-login', 'Verify login without starting the server')
|
|
18
18
|
.option('--read-only', 'Start server in read-only mode, disabling write operations')
|
|
19
19
|
.option('--http [port]', 'Use Streamable HTTP transport instead of stdio (optionally specify port, default: 3000)')
|
|
20
|
-
.option('--enable-auth-tools', 'Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)')
|
|
20
|
+
.option('--enable-auth-tools', 'Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)')
|
|
21
|
+
.option('--enabled-tools <pattern>', 'Filter tools using regex pattern (e.g., "excel|contact" to enable Excel and Contact tools)');
|
|
21
22
|
export function parseArgs() {
|
|
22
23
|
program.parse();
|
|
23
24
|
const options = program.opts();
|
|
24
25
|
if (process.env.READ_ONLY === 'true' || process.env.READ_ONLY === '1') {
|
|
25
26
|
options.readOnly = true;
|
|
26
27
|
}
|
|
28
|
+
if (process.env.ENABLED_TOOLS) {
|
|
29
|
+
options.enabledTools = process.env.ENABLED_TOOLS;
|
|
30
|
+
}
|
|
27
31
|
return options;
|
|
28
32
|
}
|
package/dist/endpoints.json
CHANGED
|
@@ -279,5 +279,110 @@
|
|
|
279
279
|
"method": "get",
|
|
280
280
|
"toolName": "get-current-user",
|
|
281
281
|
"scopes": ["User.Read"]
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"pathPattern": "/me/chats",
|
|
285
|
+
"method": "get",
|
|
286
|
+
"toolName": "list-chats",
|
|
287
|
+
"scopes": ["Chat.Read"],
|
|
288
|
+
"requiresWorkAccount": true
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"pathPattern": "/chats/{chat-id}",
|
|
292
|
+
"method": "get",
|
|
293
|
+
"toolName": "get-chat",
|
|
294
|
+
"scopes": ["Chat.Read"],
|
|
295
|
+
"requiresWorkAccount": true
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"pathPattern": "/chats/{chat-id}/messages",
|
|
299
|
+
"method": "get",
|
|
300
|
+
"toolName": "list-chat-messages",
|
|
301
|
+
"scopes": ["ChatMessage.Read"],
|
|
302
|
+
"requiresWorkAccount": true
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}",
|
|
306
|
+
"method": "get",
|
|
307
|
+
"toolName": "get-chat-message",
|
|
308
|
+
"scopes": ["ChatMessage.Read"],
|
|
309
|
+
"requiresWorkAccount": true
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"pathPattern": "/chats/{chat-id}/messages",
|
|
313
|
+
"method": "post",
|
|
314
|
+
"toolName": "send-chat-message",
|
|
315
|
+
"scopes": ["ChatMessage.Send"],
|
|
316
|
+
"requiresWorkAccount": true
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"pathPattern": "/me/joinedTeams",
|
|
320
|
+
"method": "get",
|
|
321
|
+
"toolName": "list-joined-teams",
|
|
322
|
+
"scopes": ["Team.ReadBasic.All"],
|
|
323
|
+
"requiresWorkAccount": true
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"pathPattern": "/teams/{team-id}",
|
|
327
|
+
"method": "get",
|
|
328
|
+
"toolName": "get-team",
|
|
329
|
+
"scopes": ["Team.ReadBasic.All"],
|
|
330
|
+
"requiresWorkAccount": true
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"pathPattern": "/teams/{team-id}/channels",
|
|
334
|
+
"method": "get",
|
|
335
|
+
"toolName": "list-team-channels",
|
|
336
|
+
"scopes": ["Channel.ReadBasic.All"],
|
|
337
|
+
"requiresWorkAccount": true
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"pathPattern": "/teams/{team-id}/channels/{channel-id}",
|
|
341
|
+
"method": "get",
|
|
342
|
+
"toolName": "get-team-channel",
|
|
343
|
+
"scopes": ["Channel.ReadBasic.All"],
|
|
344
|
+
"requiresWorkAccount": true
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages",
|
|
348
|
+
"method": "get",
|
|
349
|
+
"toolName": "list-channel-messages",
|
|
350
|
+
"scopes": ["ChannelMessage.Read.All"],
|
|
351
|
+
"requiresWorkAccount": true
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}",
|
|
355
|
+
"method": "get",
|
|
356
|
+
"toolName": "get-channel-message",
|
|
357
|
+
"scopes": ["ChannelMessage.Read.All"],
|
|
358
|
+
"requiresWorkAccount": true
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
"pathPattern": "/teams/{team-id}/channels/{channel-id}/messages",
|
|
362
|
+
"method": "post",
|
|
363
|
+
"toolName": "send-channel-message",
|
|
364
|
+
"scopes": ["ChannelMessage.Send"],
|
|
365
|
+
"requiresWorkAccount": true
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"pathPattern": "/teams/{team-id}/members",
|
|
369
|
+
"method": "get",
|
|
370
|
+
"toolName": "list-team-members",
|
|
371
|
+
"scopes": ["TeamMember.Read.All"],
|
|
372
|
+
"requiresWorkAccount": true
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/replies",
|
|
376
|
+
"method": "get",
|
|
377
|
+
"toolName": "list-chat-message-replies",
|
|
378
|
+
"scopes": ["ChatMessage.Read"],
|
|
379
|
+
"requiresWorkAccount": true
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/replies",
|
|
383
|
+
"method": "post",
|
|
384
|
+
"toolName": "reply-to-chat-message",
|
|
385
|
+
"scopes": ["ChatMessage.Send"],
|
|
386
|
+
"requiresWorkAccount": true
|
|
282
387
|
}
|
|
283
388
|
]
|