@softeria/ms-365-mcp-server 0.11.1 → 0.11.3

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.
@@ -77,4 +77,108 @@ export function registerAuthTools(server, authManager) {
77
77
  ],
78
78
  };
79
79
  });
80
+ server.tool('list-accounts', {}, async () => {
81
+ try {
82
+ const accounts = await authManager.listAccounts();
83
+ const selectedAccountId = authManager.getSelectedAccountId();
84
+ const result = accounts.map(account => ({
85
+ id: account.homeAccountId,
86
+ username: account.username,
87
+ name: account.name,
88
+ selected: account.homeAccountId === selectedAccountId
89
+ }));
90
+ return {
91
+ content: [
92
+ {
93
+ type: 'text',
94
+ text: JSON.stringify({ accounts: result }),
95
+ },
96
+ ],
97
+ };
98
+ }
99
+ catch (error) {
100
+ return {
101
+ content: [
102
+ {
103
+ type: 'text',
104
+ text: JSON.stringify({ error: `Failed to list accounts: ${error.message}` }),
105
+ },
106
+ ],
107
+ };
108
+ }
109
+ });
110
+ server.tool('select-account', {
111
+ accountId: z.string().describe('The account ID to select'),
112
+ }, async ({ accountId }) => {
113
+ try {
114
+ const success = await authManager.selectAccount(accountId);
115
+ if (success) {
116
+ return {
117
+ content: [
118
+ {
119
+ type: 'text',
120
+ text: JSON.stringify({ message: `Selected account: ${accountId}` }),
121
+ },
122
+ ],
123
+ };
124
+ }
125
+ else {
126
+ return {
127
+ content: [
128
+ {
129
+ type: 'text',
130
+ text: JSON.stringify({ error: `Account not found: ${accountId}` }),
131
+ },
132
+ ],
133
+ };
134
+ }
135
+ }
136
+ catch (error) {
137
+ return {
138
+ content: [
139
+ {
140
+ type: 'text',
141
+ text: JSON.stringify({ error: `Failed to select account: ${error.message}` }),
142
+ },
143
+ ],
144
+ };
145
+ }
146
+ });
147
+ server.tool('remove-account', {
148
+ accountId: z.string().describe('The account ID to remove'),
149
+ }, async ({ accountId }) => {
150
+ try {
151
+ const success = await authManager.removeAccount(accountId);
152
+ if (success) {
153
+ return {
154
+ content: [
155
+ {
156
+ type: 'text',
157
+ text: JSON.stringify({ message: `Removed account: ${accountId}` }),
158
+ },
159
+ ],
160
+ };
161
+ }
162
+ else {
163
+ return {
164
+ content: [
165
+ {
166
+ type: 'text',
167
+ text: JSON.stringify({ error: `Account not found: ${accountId}` }),
168
+ },
169
+ ],
170
+ };
171
+ }
172
+ }
173
+ catch (error) {
174
+ return {
175
+ content: [
176
+ {
177
+ type: 'text',
178
+ text: JSON.stringify({ error: `Failed to remove account: ${error.message}` }),
179
+ },
180
+ ],
181
+ };
182
+ }
183
+ });
80
184
  }
package/dist/auth.js CHANGED
@@ -4,13 +4,18 @@ import { fileURLToPath } from 'url';
4
4
  import path from 'path';
5
5
  import fs from 'fs';
6
6
  import logger from './logger.js';
7
- const endpoints = await import('./endpoints.json', {
8
- with: { type: 'json' },
9
- });
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const endpointsPath = path.join(__dirname, 'endpoints.json');
10
+ const endpoints = {
11
+ default: JSON.parse(fs.readFileSync(endpointsPath, 'utf8')),
12
+ };
10
13
  const SERVICE_NAME = 'ms-365-mcp-server';
11
14
  const TOKEN_CACHE_ACCOUNT = 'msal-token-cache';
15
+ const SELECTED_ACCOUNT_KEY = 'selected-account';
12
16
  const FALLBACK_DIR = path.dirname(fileURLToPath(import.meta.url));
13
17
  const FALLBACK_PATH = path.join(FALLBACK_DIR, '..', '.token-cache.json');
18
+ const SELECTED_ACCOUNT_PATH = path.join(FALLBACK_DIR, '..', '.selected-account.json');
14
19
  const DEFAULT_CONFIG = {
15
20
  auth: {
16
21
  clientId: process.env.MS365_MCP_CLIENT_ID || '084a3e9f-a9f4-43f7-89f9-d229cf97853e',
@@ -53,6 +58,7 @@ class AuthManager {
53
58
  this.msalApp = new PublicClientApplication(this.config);
54
59
  this.accessToken = null;
55
60
  this.tokenExpiry = null;
61
+ this.selectedAccountId = null;
56
62
  const oauthTokenFromEnv = process.env.MS365_MCP_OAUTH_TOKEN;
57
63
  this.oauthToken = oauthTokenFromEnv ?? null;
58
64
  this.isOAuthMode = oauthTokenFromEnv != null;
@@ -75,11 +81,38 @@ class AuthManager {
75
81
  if (cacheData) {
76
82
  this.msalApp.getTokenCache().deserialize(cacheData);
77
83
  }
84
+ // Load selected account
85
+ await this.loadSelectedAccount();
78
86
  }
79
87
  catch (error) {
80
88
  logger.error(`Error loading token cache: ${error.message}`);
81
89
  }
82
90
  }
91
+ async loadSelectedAccount() {
92
+ try {
93
+ let selectedAccountData;
94
+ try {
95
+ const cachedData = await keytar.getPassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY);
96
+ if (cachedData) {
97
+ selectedAccountData = cachedData;
98
+ }
99
+ }
100
+ catch (keytarError) {
101
+ logger.warn(`Keychain access failed for selected account, falling back to file storage: ${keytarError.message}`);
102
+ }
103
+ if (!selectedAccountData && fs.existsSync(SELECTED_ACCOUNT_PATH)) {
104
+ selectedAccountData = fs.readFileSync(SELECTED_ACCOUNT_PATH, 'utf8');
105
+ }
106
+ if (selectedAccountData) {
107
+ const parsed = JSON.parse(selectedAccountData);
108
+ this.selectedAccountId = parsed.accountId;
109
+ logger.info(`Loaded selected account: ${this.selectedAccountId}`);
110
+ }
111
+ }
112
+ catch (error) {
113
+ logger.error(`Error loading selected account: ${error.message}`);
114
+ }
115
+ }
83
116
  async saveTokenCache() {
84
117
  try {
85
118
  const cacheData = this.msalApp.getTokenCache().serialize();
@@ -95,6 +128,21 @@ class AuthManager {
95
128
  logger.error(`Error saving token cache: ${error.message}`);
96
129
  }
97
130
  }
131
+ async saveSelectedAccount() {
132
+ try {
133
+ const selectedAccountData = JSON.stringify({ accountId: this.selectedAccountId });
134
+ try {
135
+ await keytar.setPassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY, selectedAccountData);
136
+ }
137
+ catch (keytarError) {
138
+ logger.warn(`Keychain save failed for selected account, falling back to file storage: ${keytarError.message}`);
139
+ fs.writeFileSync(SELECTED_ACCOUNT_PATH, selectedAccountData);
140
+ }
141
+ }
142
+ catch (error) {
143
+ logger.error(`Error saving selected account: ${error.message}`);
144
+ }
145
+ }
98
146
  async setOAuthToken(token) {
99
147
  this.oauthToken = token;
100
148
  this.isOAuthMode = true;
@@ -106,10 +154,10 @@ class AuthManager {
106
154
  if (this.accessToken && this.tokenExpiry && this.tokenExpiry > Date.now() && !forceRefresh) {
107
155
  return this.accessToken;
108
156
  }
109
- const accounts = await this.msalApp.getTokenCache().getAllAccounts();
110
- if (accounts.length > 0) {
157
+ const currentAccount = await this.getCurrentAccount();
158
+ if (currentAccount) {
111
159
  const silentRequest = {
112
- account: accounts[0],
160
+ account: currentAccount,
113
161
  scopes: this.scopes,
114
162
  };
115
163
  try {
@@ -125,6 +173,22 @@ class AuthManager {
125
173
  }
126
174
  throw new Error('No valid token found');
127
175
  }
176
+ async getCurrentAccount() {
177
+ const accounts = await this.msalApp.getTokenCache().getAllAccounts();
178
+ if (accounts.length === 0) {
179
+ return null;
180
+ }
181
+ // If a specific account is selected, find it
182
+ if (this.selectedAccountId) {
183
+ const selectedAccount = accounts.find((account) => account.homeAccountId === this.selectedAccountId);
184
+ if (selectedAccount) {
185
+ return selectedAccount;
186
+ }
187
+ logger.warn(`Selected account ${this.selectedAccountId} not found, falling back to first account`);
188
+ }
189
+ // Fall back to first account (backward compatibility)
190
+ return accounts[0];
191
+ }
128
192
  async acquireTokenByDeviceCode(hack) {
129
193
  const deviceCodeRequest = {
130
194
  scopes: this.scopes,
@@ -147,6 +211,12 @@ class AuthManager {
147
211
  logger.info('Device code login successful');
148
212
  this.accessToken = response?.accessToken || null;
149
213
  this.tokenExpiry = response?.expiresOn ? new Date(response.expiresOn).getTime() : null;
214
+ // Set the newly authenticated account as selected if no account is currently selected
215
+ if (!this.selectedAccountId && response?.account) {
216
+ this.selectedAccountId = response.account.homeAccountId;
217
+ await this.saveSelectedAccount();
218
+ logger.info(`Auto-selected new account: ${response.account.username}`);
219
+ }
150
220
  await this.saveTokenCache();
151
221
  return this.accessToken;
152
222
  }
@@ -218,8 +288,10 @@ class AuthManager {
218
288
  }
219
289
  this.accessToken = null;
220
290
  this.tokenExpiry = null;
291
+ this.selectedAccountId = null;
221
292
  try {
222
293
  await keytar.deletePassword(SERVICE_NAME, TOKEN_CACHE_ACCOUNT);
294
+ await keytar.deletePassword(SERVICE_NAME, SELECTED_ACCOUNT_KEY);
223
295
  }
224
296
  catch (keytarError) {
225
297
  logger.warn(`Keychain deletion failed: ${keytarError.message}`);
@@ -227,6 +299,9 @@ class AuthManager {
227
299
  if (fs.existsSync(FALLBACK_PATH)) {
228
300
  fs.unlinkSync(FALLBACK_PATH);
229
301
  }
302
+ if (fs.existsSync(SELECTED_ACCOUNT_PATH)) {
303
+ fs.unlinkSync(SELECTED_ACCOUNT_PATH);
304
+ }
230
305
  return true;
231
306
  }
232
307
  catch (error) {
@@ -236,8 +311,8 @@ class AuthManager {
236
311
  }
237
312
  async hasWorkAccountPermissions() {
238
313
  try {
239
- const accounts = await this.msalApp.getTokenCache().getAllAccounts();
240
- if (accounts.length === 0) {
314
+ const currentAccount = await this.getCurrentAccount();
315
+ if (!currentAccount) {
241
316
  return false;
242
317
  }
243
318
  const workScopes = endpoints.default
@@ -246,7 +321,7 @@ class AuthManager {
246
321
  try {
247
322
  await this.msalApp.acquireTokenSilent({
248
323
  scopes: workScopes.slice(0, 1),
249
- account: accounts[0],
324
+ account: currentAccount,
250
325
  });
251
326
  return true;
252
327
  }
@@ -287,6 +362,12 @@ class AuthManager {
287
362
  this.accessToken = response?.accessToken || null;
288
363
  this.tokenExpiry = response?.expiresOn ? new Date(response.expiresOn).getTime() : null;
289
364
  this.scopes = allScopes;
365
+ // Update selected account if this is a new account
366
+ if (response?.account) {
367
+ this.selectedAccountId = response.account.homeAccountId;
368
+ await this.saveSelectedAccount();
369
+ logger.info(`Updated selected account after scope expansion: ${response.account.username}`);
370
+ }
290
371
  await this.saveTokenCache();
291
372
  return true;
292
373
  }
@@ -295,6 +376,71 @@ class AuthManager {
295
376
  return false;
296
377
  }
297
378
  }
379
+ // Multi-account support methods
380
+ async listAccounts() {
381
+ return await this.msalApp.getTokenCache().getAllAccounts();
382
+ }
383
+ async selectAccount(accountId) {
384
+ const accounts = await this.listAccounts();
385
+ const account = accounts.find((acc) => acc.homeAccountId === accountId);
386
+ if (!account) {
387
+ logger.error(`Account with ID ${accountId} not found`);
388
+ return false;
389
+ }
390
+ this.selectedAccountId = accountId;
391
+ await this.saveSelectedAccount();
392
+ // Clear cached tokens to force refresh with new account
393
+ this.accessToken = null;
394
+ this.tokenExpiry = null;
395
+ logger.info(`Selected account: ${account.username} (${accountId})`);
396
+ return true;
397
+ }
398
+ async getTokenForAccount(accountId) {
399
+ const accounts = await this.listAccounts();
400
+ const account = accounts.find((acc) => acc.homeAccountId === accountId);
401
+ if (!account) {
402
+ throw new Error(`Account with ID ${accountId} not found`);
403
+ }
404
+ const silentRequest = {
405
+ account: account,
406
+ scopes: this.scopes,
407
+ };
408
+ try {
409
+ const response = await this.msalApp.acquireTokenSilent(silentRequest);
410
+ return response.accessToken;
411
+ }
412
+ catch (error) {
413
+ logger.error(`Failed to get token for account ${accountId}: ${error.message}`);
414
+ throw error;
415
+ }
416
+ }
417
+ async removeAccount(accountId) {
418
+ const accounts = await this.listAccounts();
419
+ const account = accounts.find((acc) => acc.homeAccountId === accountId);
420
+ if (!account) {
421
+ logger.error(`Account with ID ${accountId} not found`);
422
+ return false;
423
+ }
424
+ try {
425
+ await this.msalApp.getTokenCache().removeAccount(account);
426
+ // If this was the selected account, clear the selection
427
+ if (this.selectedAccountId === accountId) {
428
+ this.selectedAccountId = null;
429
+ await this.saveSelectedAccount();
430
+ this.accessToken = null;
431
+ this.tokenExpiry = null;
432
+ }
433
+ logger.info(`Removed account: ${account.username} (${accountId})`);
434
+ return true;
435
+ }
436
+ catch (error) {
437
+ logger.error(`Failed to remove account ${accountId}: ${error.message}`);
438
+ return false;
439
+ }
440
+ }
441
+ getSelectedAccountId() {
442
+ return this.selectedAccountId;
443
+ }
298
444
  requiresWorkAccountScope(toolName) {
299
445
  const endpoint = endpoints.default.find((e) => e.toolName === toolName);
300
446
  return endpoint?.requiresWorkAccount === true;
package/dist/cli.js CHANGED
@@ -15,6 +15,9 @@ program
15
15
  .option('--login', 'Login using device code flow')
16
16
  .option('--logout', 'Log out and clear saved credentials')
17
17
  .option('--verify-login', 'Verify login without starting the server')
18
+ .option('--list-accounts', 'List all cached accounts')
19
+ .option('--select-account <accountId>', 'Select a specific account by ID')
20
+ .option('--remove-account <accountId>', 'Remove a specific account by ID')
18
21
  .option('--read-only', 'Start server in read-only mode, disabling write operations')
19
22
  .option('--http [port]', 'Use Streamable HTTP transport instead of stdio (optionally specify port, default: 3000)')
20
23
  .option('--enable-auth-tools', 'Enable login/logout tools when using HTTP mode (disabled by default in HTTP mode)')
@@ -11,14 +11,39 @@ class GraphClient {
11
11
  this.accessToken = accessToken;
12
12
  this.refreshToken = refreshToken || null;
13
13
  }
14
+ async getCurrentAccountId() {
15
+ const currentAccount = await this.authManager.getCurrentAccount();
16
+ return currentAccount?.homeAccountId || null;
17
+ }
18
+ getAccountSessions(accountId) {
19
+ if (!this.sessions.has(accountId)) {
20
+ this.sessions.set(accountId, new Map());
21
+ }
22
+ return this.sessions.get(accountId);
23
+ }
24
+ async getSessionForFile(filePath) {
25
+ const accountId = await this.getCurrentAccountId();
26
+ if (!accountId)
27
+ return null;
28
+ const accountSessions = this.getAccountSessions(accountId);
29
+ return accountSessions.get(filePath) || null;
30
+ }
31
+ async setSessionForFile(filePath, sessionId) {
32
+ const accountId = await this.getCurrentAccountId();
33
+ if (!accountId)
34
+ return;
35
+ const accountSessions = this.getAccountSessions(accountId);
36
+ accountSessions.set(filePath, sessionId);
37
+ }
14
38
  async createSession(filePath) {
15
39
  try {
16
40
  if (!filePath) {
17
41
  logger.error('No file path provided for Excel session');
18
42
  return null;
19
43
  }
20
- if (this.sessions.has(filePath)) {
21
- return this.sessions.get(filePath) || null;
44
+ const existingSession = await this.getSessionForFile(filePath);
45
+ if (existingSession) {
46
+ return existingSession;
22
47
  }
23
48
  logger.info(`Creating new Excel session for file: ${filePath}`);
24
49
  const accessToken = await this.authManager.getToken();
@@ -37,7 +62,7 @@ class GraphClient {
37
62
  }
38
63
  const result = await response.json();
39
64
  logger.info(`Session created successfully for file: ${filePath}`);
40
- this.sessions.set(filePath, result.id);
65
+ await this.setSessionForFile(filePath, result.id);
41
66
  return result.id;
42
67
  }
43
68
  catch (error) {
@@ -116,7 +141,7 @@ class GraphClient {
116
141
  !endpoint.startsWith('/teams') &&
117
142
  !endpoint.startsWith('/chats') &&
118
143
  !endpoint.startsWith('/planner')) {
119
- sessionId = this.sessions.get(options.excelFile) || null;
144
+ sessionId = await this.getSessionForFile(options.excelFile);
120
145
  if (!sessionId) {
121
146
  sessionId = await this.createSessionWithToken(options.excelFile, accessToken);
122
147
  }
@@ -166,8 +191,9 @@ class GraphClient {
166
191
  logger.error('No file path provided for Excel session');
167
192
  return null;
168
193
  }
169
- if (this.sessions.has(filePath)) {
170
- return this.sessions.get(filePath) || null;
194
+ const existingSession = await this.getSessionForFile(filePath);
195
+ if (existingSession) {
196
+ return existingSession;
171
197
  }
172
198
  logger.info(`Creating new Excel session for file: ${filePath}`);
173
199
  const response = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:${filePath}:/workbook/createSession`, {
@@ -185,7 +211,7 @@ class GraphClient {
185
211
  }
186
212
  const result = await response.json();
187
213
  logger.info(`Session created successfully for file: ${filePath}`);
188
- this.sessions.set(filePath, result.id);
214
+ await this.setSessionForFile(filePath, result.id);
189
215
  return result.id;
190
216
  }
191
217
  catch (error) {
@@ -236,7 +262,7 @@ class GraphClient {
236
262
  !endpoint.startsWith('/chats') &&
237
263
  !endpoint.startsWith('/planner') &&
238
264
  !endpoint.startsWith('/sites')) {
239
- sessionId = this.sessions.get(options.excelFile) || null;
265
+ sessionId = await this.getSessionForFile(options.excelFile);
240
266
  if (!sessionId) {
241
267
  sessionId = await this.createSession(options.excelFile);
242
268
  }
@@ -407,7 +433,8 @@ class GraphClient {
407
433
  }
408
434
  }
409
435
  async closeSession(filePath) {
410
- if (!filePath || !this.sessions.has(filePath)) {
436
+ const sessionId = await this.getSessionForFile(filePath);
437
+ if (!filePath || !sessionId) {
411
438
  return {
412
439
  content: [
413
440
  {
@@ -417,7 +444,6 @@ class GraphClient {
417
444
  ],
418
445
  };
419
446
  }
420
- const sessionId = this.sessions.get(filePath);
421
447
  try {
422
448
  const accessToken = await this.authManager.getToken();
423
449
  const response = await fetch(`https://graph.microsoft.com/v1.0/me/drive/root:${filePath}:/workbook/closeSession`, {
@@ -429,7 +455,11 @@ class GraphClient {
429
455
  },
430
456
  });
431
457
  if (response.ok) {
432
- this.sessions.delete(filePath);
458
+ const accountId = await this.getCurrentAccountId();
459
+ if (accountId) {
460
+ const accountSessions = this.getAccountSessions(accountId);
461
+ accountSessions.delete(filePath);
462
+ }
433
463
  return {
434
464
  content: [
435
465
  {
@@ -458,9 +488,13 @@ class GraphClient {
458
488
  }
459
489
  async closeAllSessions() {
460
490
  const results = [];
461
- for (const [filePath] of this.sessions) {
462
- const result = await this.closeSession(filePath);
463
- results.push(result);
491
+ const accountId = await this.getCurrentAccountId();
492
+ if (accountId) {
493
+ const accountSessions = this.getAccountSessions(accountId);
494
+ for (const [filePath] of accountSessions) {
495
+ const result = await this.closeSession(filePath);
496
+ results.push(result);
497
+ }
464
498
  }
465
499
  return {
466
500
  content: [
package/dist/index.js CHANGED
@@ -40,6 +40,40 @@ async function main() {
40
40
  console.log(JSON.stringify({ message: 'Logged out successfully' }));
41
41
  process.exit(0);
42
42
  }
43
+ if (args.listAccounts) {
44
+ const accounts = await authManager.listAccounts();
45
+ const selectedAccountId = authManager.getSelectedAccountId();
46
+ const result = accounts.map(account => ({
47
+ id: account.homeAccountId,
48
+ username: account.username,
49
+ name: account.name,
50
+ selected: account.homeAccountId === selectedAccountId
51
+ }));
52
+ console.log(JSON.stringify({ accounts: result }));
53
+ process.exit(0);
54
+ }
55
+ if (args.selectAccount) {
56
+ const success = await authManager.selectAccount(args.selectAccount);
57
+ if (success) {
58
+ console.log(JSON.stringify({ message: `Selected account: ${args.selectAccount}` }));
59
+ }
60
+ else {
61
+ console.log(JSON.stringify({ error: `Account not found: ${args.selectAccount}` }));
62
+ process.exit(1);
63
+ }
64
+ process.exit(0);
65
+ }
66
+ if (args.removeAccount) {
67
+ const success = await authManager.removeAccount(args.removeAccount);
68
+ if (success) {
69
+ console.log(JSON.stringify({ message: `Removed account: ${args.removeAccount}` }));
70
+ }
71
+ else {
72
+ console.log(JSON.stringify({ error: `Account not found: ${args.removeAccount}` }));
73
+ process.exit(1);
74
+ }
75
+ process.exit(0);
76
+ }
43
77
  const server = new MicrosoftGraphServer(authManager, args);
44
78
  await server.initialize(version);
45
79
  await server.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "Microsoft 365 MCP Server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,490 +0,0 @@
1
- [
2
- {
3
- "pathPattern": "/me/messages",
4
- "method": "get",
5
- "toolName": "list-mail-messages",
6
- "scopes": ["Mail.Read"]
7
- },
8
- {
9
- "pathPattern": "/me/mailFolders",
10
- "method": "get",
11
- "toolName": "list-mail-folders",
12
- "scopes": ["Mail.Read"]
13
- },
14
- {
15
- "pathPattern": "/me/mailFolders/{mailFolder-id}/messages",
16
- "method": "get",
17
- "toolName": "list-mail-folder-messages",
18
- "scopes": ["Mail.Read"]
19
- },
20
- {
21
- "pathPattern": "/me/messages/{message-id}",
22
- "method": "get",
23
- "toolName": "get-mail-message",
24
- "scopes": ["Mail.Read"]
25
- },
26
- {
27
- "pathPattern": "/me/sendMail",
28
- "method": "post",
29
- "toolName": "send-mail",
30
- "scopes": ["Mail.Send"]
31
- },
32
- {
33
- "pathPattern": "/me/messages",
34
- "method": "post",
35
- "toolName": "create-draft-email",
36
- "scopes": ["Mail.ReadWrite"]
37
- },
38
- {
39
- "pathPattern": "/me/messages/{message-id}",
40
- "method": "delete",
41
- "toolName": "delete-mail-message",
42
- "scopes": ["Mail.ReadWrite"]
43
- },
44
- {
45
- "pathPattern": "/me/messages/{message-id}/move",
46
- "method": "post",
47
- "toolName": "move-mail-message",
48
- "scopes": ["Mail.ReadWrite"]
49
- },
50
- {
51
- "pathPattern": "/me/events",
52
- "method": "get",
53
- "toolName": "list-calendar-events",
54
- "scopes": ["Calendars.Read"]
55
- },
56
- {
57
- "pathPattern": "/me/events/{event-id}",
58
- "method": "get",
59
- "toolName": "get-calendar-event",
60
- "scopes": ["Calendars.Read"]
61
- },
62
- {
63
- "pathPattern": "/me/events",
64
- "method": "post",
65
- "toolName": "create-calendar-event",
66
- "scopes": ["Calendars.ReadWrite"]
67
- },
68
- {
69
- "pathPattern": "/me/events/{event-id}",
70
- "method": "patch",
71
- "toolName": "update-calendar-event",
72
- "scopes": ["Calendars.ReadWrite"]
73
- },
74
- {
75
- "pathPattern": "/me/events/{event-id}",
76
- "method": "delete",
77
- "toolName": "delete-calendar-event",
78
- "scopes": ["Calendars.ReadWrite"]
79
- },
80
- {
81
- "pathPattern": "/me/calendarView",
82
- "method": "get",
83
- "toolName": "get-calendar-view",
84
- "scopes": ["Calendars.Read"]
85
- },
86
- {
87
- "pathPattern": "/me/calendars",
88
- "method": "get",
89
- "toolName": "list-calendars",
90
- "scopes": ["Calendars.Read"]
91
- },
92
- {
93
- "pathPattern": "/me/drives",
94
- "method": "get",
95
- "toolName": "list-drives",
96
- "scopes": ["Files.Read"]
97
- },
98
- {
99
- "pathPattern": "/drives/{drive-id}/root",
100
- "method": "get",
101
- "toolName": "get-drive-root-item",
102
- "scopes": ["Files.Read"]
103
- },
104
- {
105
- "pathPattern": "/drives/{drive-id}/root",
106
- "method": "get",
107
- "toolName": "get-root-folder",
108
- "scopes": ["Files.Read"]
109
- },
110
- {
111
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/children",
112
- "method": "get",
113
- "toolName": "list-folder-files",
114
- "scopes": ["Files.Read"]
115
- },
116
- {
117
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/children/{driveItem-id1}/content",
118
- "method": "get",
119
- "toolName": "download-onedrive-file-content",
120
- "scopes": ["Files.Read"]
121
- },
122
- {
123
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}",
124
- "method": "delete",
125
- "toolName": "delete-onedrive-file",
126
- "scopes": ["Files.ReadWrite"]
127
- },
128
- {
129
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/content",
130
- "method": "put",
131
- "toolName": "upload-file-content",
132
- "scopes": ["Files.ReadWrite"]
133
- },
134
- {
135
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/charts/add",
136
- "method": "post",
137
- "toolName": "create-excel-chart",
138
- "isExcelOp": true,
139
- "scopes": ["Files.ReadWrite"]
140
- },
141
- {
142
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range()/format",
143
- "method": "patch",
144
- "toolName": "format-excel-range",
145
- "isExcelOp": true,
146
- "scopes": ["Files.ReadWrite"]
147
- },
148
- {
149
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range()/sort",
150
- "method": "patch",
151
- "toolName": "sort-excel-range",
152
- "isExcelOp": true,
153
- "scopes": ["Files.ReadWrite"]
154
- },
155
- {
156
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets/{workbookWorksheet-id}/range(address='{address}')",
157
- "method": "get",
158
- "toolName": "get-excel-range",
159
- "isExcelOp": true,
160
- "scopes": ["Files.Read"]
161
- },
162
- {
163
- "pathPattern": "/drives/{drive-id}/items/{driveItem-id}/workbook/worksheets",
164
- "method": "get",
165
- "toolName": "list-excel-worksheets",
166
- "isExcelOp": true,
167
- "scopes": ["Files.Read"]
168
- },
169
- {
170
- "pathPattern": "/me/onenote/notebooks",
171
- "method": "get",
172
- "toolName": "list-onenote-notebooks",
173
- "scopes": ["Notes.Read"]
174
- },
175
- {
176
- "pathPattern": "/me/onenote/notebooks/{notebook-id}/sections",
177
- "method": "get",
178
- "toolName": "list-onenote-notebook-sections",
179
- "scopes": ["Notes.Read"]
180
- },
181
- {
182
- "pathPattern": "/me/onenote/sections/{onenoteSection-id}/pages",
183
- "method": "get",
184
- "toolName": "list-onenote-section-pages",
185
- "scopes": ["Notes.Read"]
186
- },
187
- {
188
- "pathPattern": "/me/onenote/pages/{onenotePage-id}/content",
189
- "method": "get",
190
- "toolName": "get-onenote-page-content",
191
- "scopes": ["Notes.Read"]
192
- },
193
- {
194
- "pathPattern": "/me/onenote/pages",
195
- "method": "post",
196
- "toolName": "create-onenote-page",
197
- "scopes": ["Notes.Create"]
198
- },
199
- {
200
- "pathPattern": "/me/todo/lists",
201
- "method": "get",
202
- "toolName": "list-todo-task-lists",
203
- "scopes": ["Tasks.Read"]
204
- },
205
- {
206
- "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks",
207
- "method": "get",
208
- "toolName": "list-todo-tasks",
209
- "scopes": ["Tasks.Read"]
210
- },
211
- {
212
- "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
213
- "method": "get",
214
- "toolName": "get-todo-task",
215
- "scopes": ["Tasks.Read"]
216
- },
217
- {
218
- "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks",
219
- "method": "post",
220
- "toolName": "create-todo-task",
221
- "scopes": ["Tasks.ReadWrite"]
222
- },
223
- {
224
- "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
225
- "method": "patch",
226
- "toolName": "update-todo-task",
227
- "scopes": ["Tasks.ReadWrite"]
228
- },
229
- {
230
- "pathPattern": "/me/todo/lists/{todoTaskList-id}/tasks/{todoTask-id}",
231
- "method": "delete",
232
- "toolName": "delete-todo-task",
233
- "scopes": ["Tasks.ReadWrite"]
234
- },
235
- {
236
- "pathPattern": "/me/planner/tasks",
237
- "method": "get",
238
- "toolName": "list-planner-tasks",
239
- "scopes": ["Tasks.Read"]
240
- },
241
- {
242
- "pathPattern": "/planner/plans/{plannerPlan-id}",
243
- "method": "get",
244
- "toolName": "get-planner-plan",
245
- "scopes": ["Tasks.Read"]
246
- },
247
- {
248
- "pathPattern": "/planner/plans/{plannerPlan-id}/tasks",
249
- "method": "get",
250
- "toolName": "list-plan-tasks",
251
- "scopes": ["Tasks.Read"]
252
- },
253
- {
254
- "pathPattern": "/planner/tasks/{plannerTask-id}",
255
- "method": "get",
256
- "toolName": "get-planner-task",
257
- "scopes": ["Tasks.Read"]
258
- },
259
- {
260
- "pathPattern": "/planner/tasks",
261
- "method": "post",
262
- "toolName": "create-planner-task",
263
- "scopes": ["Tasks.ReadWrite"]
264
- },
265
- {
266
- "pathPattern": "/me/contacts",
267
- "method": "get",
268
- "toolName": "list-outlook-contacts",
269
- "scopes": ["Contacts.Read"]
270
- },
271
- {
272
- "pathPattern": "/me/contacts/{contact-id}",
273
- "method": "get",
274
- "toolName": "get-outlook-contact",
275
- "scopes": ["Contacts.Read"]
276
- },
277
- {
278
- "pathPattern": "/me/contacts",
279
- "method": "post",
280
- "toolName": "create-outlook-contact",
281
- "scopes": ["Contacts.ReadWrite"]
282
- },
283
- {
284
- "pathPattern": "/me/contacts/{contact-id}",
285
- "method": "patch",
286
- "toolName": "update-outlook-contact",
287
- "scopes": ["Contacts.ReadWrite"]
288
- },
289
- {
290
- "pathPattern": "/me/contacts/{contact-id}",
291
- "method": "delete",
292
- "toolName": "delete-outlook-contact",
293
- "scopes": ["Contacts.ReadWrite"]
294
- },
295
- {
296
- "pathPattern": "/me",
297
- "method": "get",
298
- "toolName": "get-current-user",
299
- "scopes": ["User.Read"]
300
- },
301
- {
302
- "pathPattern": "/me/chats",
303
- "method": "get",
304
- "toolName": "list-chats",
305
- "scopes": ["Chat.Read"],
306
- "requiresWorkAccount": true
307
- },
308
- {
309
- "pathPattern": "/chats/{chat-id}",
310
- "method": "get",
311
- "toolName": "get-chat",
312
- "scopes": ["Chat.Read"],
313
- "requiresWorkAccount": true
314
- },
315
- {
316
- "pathPattern": "/chats/{chat-id}/messages",
317
- "method": "get",
318
- "toolName": "list-chat-messages",
319
- "scopes": ["ChatMessage.Read"],
320
- "requiresWorkAccount": true
321
- },
322
- {
323
- "pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}",
324
- "method": "get",
325
- "toolName": "get-chat-message",
326
- "scopes": ["ChatMessage.Read"],
327
- "requiresWorkAccount": true
328
- },
329
- {
330
- "pathPattern": "/chats/{chat-id}/messages",
331
- "method": "post",
332
- "toolName": "send-chat-message",
333
- "scopes": ["ChatMessage.Send"],
334
- "requiresWorkAccount": true
335
- },
336
- {
337
- "pathPattern": "/me/joinedTeams",
338
- "method": "get",
339
- "toolName": "list-joined-teams",
340
- "scopes": ["Team.ReadBasic.All"],
341
- "requiresWorkAccount": true
342
- },
343
- {
344
- "pathPattern": "/teams/{team-id}",
345
- "method": "get",
346
- "toolName": "get-team",
347
- "scopes": ["Team.ReadBasic.All"],
348
- "requiresWorkAccount": true
349
- },
350
- {
351
- "pathPattern": "/teams/{team-id}/channels",
352
- "method": "get",
353
- "toolName": "list-team-channels",
354
- "scopes": ["Channel.ReadBasic.All"],
355
- "requiresWorkAccount": true
356
- },
357
- {
358
- "pathPattern": "/teams/{team-id}/channels/{channel-id}",
359
- "method": "get",
360
- "toolName": "get-team-channel",
361
- "scopes": ["Channel.ReadBasic.All"],
362
- "requiresWorkAccount": true
363
- },
364
- {
365
- "pathPattern": "/teams/{team-id}/channels/{channel-id}/messages",
366
- "method": "get",
367
- "toolName": "list-channel-messages",
368
- "scopes": ["ChannelMessage.Read.All"],
369
- "requiresWorkAccount": true
370
- },
371
- {
372
- "pathPattern": "/teams/{team-id}/channels/{channel-id}/messages/{chatMessage-id}",
373
- "method": "get",
374
- "toolName": "get-channel-message",
375
- "scopes": ["ChannelMessage.Read.All"],
376
- "requiresWorkAccount": true
377
- },
378
- {
379
- "pathPattern": "/teams/{team-id}/channels/{channel-id}/messages",
380
- "method": "post",
381
- "toolName": "send-channel-message",
382
- "scopes": ["ChannelMessage.Send"],
383
- "requiresWorkAccount": true
384
- },
385
- {
386
- "pathPattern": "/teams/{team-id}/members",
387
- "method": "get",
388
- "toolName": "list-team-members",
389
- "scopes": ["TeamMember.Read.All"],
390
- "requiresWorkAccount": true
391
- },
392
- {
393
- "pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/replies",
394
- "method": "get",
395
- "toolName": "list-chat-message-replies",
396
- "scopes": ["ChatMessage.Read"],
397
- "requiresWorkAccount": true
398
- },
399
- {
400
- "pathPattern": "/chats/{chat-id}/messages/{chatMessage-id}/replies",
401
- "method": "post",
402
- "toolName": "reply-to-chat-message",
403
- "scopes": ["ChatMessage.Send"],
404
- "requiresWorkAccount": true
405
- },
406
- {
407
- "pathPattern": "/sites",
408
- "method": "get",
409
- "toolName": "search-sharepoint-sites",
410
- "scopes": ["Sites.Read.All"],
411
- "requiresWorkAccount": true
412
- },
413
- {
414
- "pathPattern": "/sites/{site-id}",
415
- "method": "get",
416
- "toolName": "get-sharepoint-site",
417
- "scopes": ["Sites.Read.All"],
418
- "requiresWorkAccount": true
419
- },
420
- {
421
- "pathPattern": "/sites/{site-id}/drives",
422
- "method": "get",
423
- "toolName": "list-sharepoint-site-drives",
424
- "scopes": ["Sites.Read.All"],
425
- "requiresWorkAccount": true
426
- },
427
- {
428
- "pathPattern": "/sites/{site-id}/drives/{drive-id}",
429
- "method": "get",
430
- "toolName": "get-sharepoint-site-drive-by-id",
431
- "scopes": ["Sites.Read.All"],
432
- "requiresWorkAccount": true
433
- },
434
- {
435
- "pathPattern": "/sites/{site-id}/items",
436
- "method": "get",
437
- "toolName": "list-sharepoint-site-items",
438
- "scopes": ["Sites.Read.All"],
439
- "requiresWorkAccount": true
440
- },
441
- {
442
- "pathPattern": "/sites/{site-id}/items/{baseItem-id}",
443
- "method": "get",
444
- "toolName": "get-sharepoint-site-item",
445
- "scopes": ["Sites.Read.All"],
446
- "requiresWorkAccount": true
447
- },
448
- {
449
- "pathPattern": "/sites/{site-id}/lists",
450
- "method": "get",
451
- "toolName": "list-sharepoint-site-lists",
452
- "scopes": ["Sites.Read.All"],
453
- "requiresWorkAccount": true
454
- },
455
- {
456
- "pathPattern": "/sites/{site-id}/lists/{list-id}",
457
- "method": "get",
458
- "toolName": "get-sharepoint-site-list",
459
- "scopes": ["Sites.Read.All"],
460
- "requiresWorkAccount": true
461
- },
462
- {
463
- "pathPattern": "/sites/{site-id}/lists/{list-id}/items",
464
- "method": "get",
465
- "toolName": "list-sharepoint-site-list-items",
466
- "scopes": ["Sites.Read.All"],
467
- "requiresWorkAccount": true
468
- },
469
- {
470
- "pathPattern": "/sites/{site-id}/lists/{list-id}/items/{listItem-id}",
471
- "method": "get",
472
- "toolName": "get-sharepoint-site-list-item",
473
- "scopes": ["Sites.Read.All"],
474
- "requiresWorkAccount": true
475
- },
476
- {
477
- "pathPattern": "/sites/{site-id}/getByPath(path='{path}')",
478
- "method": "get",
479
- "toolName": "get-sharepoint-site-by-path",
480
- "scopes": ["Sites.Read.All"],
481
- "requiresWorkAccount": true
482
- },
483
- {
484
- "pathPattern": "/sites/delta()",
485
- "method": "get",
486
- "toolName": "get-sharepoint-sites-delta",
487
- "scopes": ["Sites.Read.All"],
488
- "requiresWorkAccount": true
489
- }
490
- ]