@softeria/ms-365-mcp-server 0.12.2 → 0.13.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 CHANGED
@@ -21,15 +21,17 @@ API.
21
21
 
22
22
  ## Supported Services & Tools
23
23
 
24
+ ### Personal Account Tools (Available by default)
25
+
24
26
  **Email (Outlook)**
25
27
  <sub>list-mail-messages, list-mail-folders, list-mail-folder-messages, get-mail-message, send-mail,
26
- delete-mail-message</sub>
28
+ delete-mail-message, create-draft-email, move-mail-message</sub>
27
29
 
28
30
  **Calendar**
29
31
  <sub>list-calendars, list-calendar-events, get-calendar-event, get-calendar-view, create-calendar-event,
30
32
  update-calendar-event, delete-calendar-event</sub>
31
33
 
32
- **OneDrive & SharePoint Files**
34
+ **OneDrive Files**
33
35
  <sub>list-drives, get-drive-root-item, list-folder-files, download-onedrive-file-content, upload-file-content,
34
36
  upload-new-file, delete-onedrive-file</sub>
35
37
 
@@ -50,21 +52,25 @@ create-onenote-page</sub>
50
52
  <sub>list-outlook-contacts, get-outlook-contact, create-outlook-contact, update-outlook-contact,
51
53
  delete-outlook-contact</sub>
52
54
 
53
- **Teams & Chats** (Work/School accounts only)
55
+ **User Profile**
56
+ <sub>get-current-user</sub>
57
+
58
+ ### Organization Account Tools (Requires --org-mode flag)
59
+
60
+ **Teams & Chats**
54
61
  <sub>list-chats, get-chat, list-chat-messages, get-chat-message, send-chat-message, list-chat-message-replies,
55
62
  reply-to-chat-message, list-joined-teams, get-team, list-team-channels, get-team-channel, list-channel-messages,
56
63
  get-channel-message, send-channel-message, list-team-members</sub>
57
64
 
58
- **SharePoint Sites** (Work/School accounts only)
65
+ **SharePoint Sites**
59
66
  <sub>search-sharepoint-sites, get-sharepoint-site, get-sharepoint-site-by-path, list-sharepoint-site-drives,
60
67
  get-sharepoint-site-drive-by-id, list-sharepoint-site-items, get-sharepoint-site-item, list-sharepoint-site-lists,
61
68
  get-sharepoint-site-list, list-sharepoint-site-list-items, get-sharepoint-site-list-item,
62
69
  get-sharepoint-sites-delta</sub>
63
70
 
64
- ### Work Scopes Issues
71
+ ## Organization/Work Mode
65
72
 
66
- If you're having issues accessing work/school features (Teams, SharePoint, etc.), you should pass the
67
- `--force-work-scopes` flag!
73
+ To access work/school features (Teams, SharePoint, etc.), enable organization mode using any of these flags:
68
74
 
69
75
  ```json
70
76
  {
@@ -74,18 +80,15 @@ If you're having issues accessing work/school features (Teams, SharePoint, etc.)
74
80
  "args": [
75
81
  "-y",
76
82
  "@softeria/ms-365-mcp-server",
77
- "--force-work-scopes"
83
+ "--org-mode"
78
84
  ]
79
85
  }
80
86
  }
81
87
  }
82
88
  ```
83
89
 
84
- While the server should attempt to force a re-login when work scopes are needed, passing the flag explicitly is safer
85
- and ensures proper scope permissions from the start.
86
-
87
- **User Profile**
88
- <sub>get-current-user</sub>
90
+ Organization mode must be enabled from the start to access work account features. Without this flag, only personal
91
+ account features (email, calendar, OneDrive, etc.) are available.
89
92
 
90
93
  ## Quick Start Example
91
94
 
@@ -226,7 +229,9 @@ The following options can be used when running ms-365-mcp-server directly from t
226
229
  --login Login using device code flow
227
230
  --logout Log out and clear saved credentials
228
231
  --verify-login Verify login without starting the server
229
- --force-work-scopes Force inclusion of work account scopes during login (includes Teams, SharePoint, etc.)
232
+ --org-mode Enable organization/work mode from start (includes Teams, SharePoint, etc.)
233
+ --work-mode Alias for --org-mode
234
+ --force-work-scopes Backwards compatibility alias for --org-mode (deprecated)
230
235
  ```
231
236
 
232
237
  ### Server Options
@@ -246,7 +251,8 @@ Environment variables:
246
251
 
247
252
  - `READ_ONLY=true|1`: Alternative to --read-only flag
248
253
  - `ENABLED_TOOLS`: Filter tools using regex pattern (alternative to --enabled-tools flag)
249
- - `MS365_MCP_FORCE_WORK_SCOPES=true|1`: Force inclusion of work account scopes (alternative to --force-work-scopes flag)
254
+ - `MS365_MCP_ORG_MODE=true|1`: Enable organization/work mode (alternative to --org-mode flag)
255
+ - `MS365_MCP_FORCE_WORK_SCOPES=true|1`: Backwards compatibility for MS365_MCP_ORG_MODE
250
256
  - `LOG_LEVEL`: Set logging level (default: 'info')
251
257
  - `SILENT=true|1`: Disable console output
252
258
  - `MS365_MCP_CLIENT_ID`: Custom Azure app client ID (defaults to built-in app)
@@ -23,6 +23,9 @@ export function createAndSaveSimplifiedOpenAPI(endpointsFile, openapiFile, opena
23
23
  const eo = e.find((ep) => ep.method.toLowerCase() === method);
24
24
  if (eo) {
25
25
  operation.operationId = eo.toolName;
26
+ if (!operation.description && operation.summary) {
27
+ operation.description = operation.summary;
28
+ }
26
29
  } else {
27
30
  delete value[method];
28
31
  }
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  function registerAuthTools(server, authManager) {
3
3
  server.tool(
4
4
  "login",
5
+ "Authenticate with Microsoft using device code flow",
5
6
  {
6
7
  force: z.boolean().default(false).describe("Force a new login even if already logged in")
7
8
  },
@@ -46,7 +47,7 @@ function registerAuthTools(server, authManager) {
46
47
  }
47
48
  }
48
49
  );
49
- server.tool("logout", {}, async () => {
50
+ server.tool("logout", "Log out from Microsoft account", {}, async () => {
50
51
  try {
51
52
  await authManager.logout();
52
53
  return {
@@ -68,7 +69,7 @@ function registerAuthTools(server, authManager) {
68
69
  };
69
70
  }
70
71
  });
71
- server.tool("verify-login", async () => {
72
+ server.tool("verify-login", "Check current Microsoft authentication status", {}, async () => {
72
73
  const testResult = await authManager.testLogin();
73
74
  return {
74
75
  content: [
@@ -79,7 +80,7 @@ function registerAuthTools(server, authManager) {
79
80
  ]
80
81
  };
81
82
  });
82
- server.tool("list-accounts", {}, async () => {
83
+ server.tool("list-accounts", "List all available Microsoft accounts", {}, async () => {
83
84
  try {
84
85
  const accounts = await authManager.listAccounts();
85
86
  const selectedAccountId = authManager.getSelectedAccountId();
@@ -110,6 +111,7 @@ function registerAuthTools(server, authManager) {
110
111
  });
111
112
  server.tool(
112
113
  "select-account",
114
+ "Select a specific Microsoft account to use",
113
115
  {
114
116
  accountId: z.string().describe("The account ID to select")
115
117
  },
@@ -140,7 +142,9 @@ function registerAuthTools(server, authManager) {
140
142
  content: [
141
143
  {
142
144
  type: "text",
143
- text: JSON.stringify({ error: `Failed to select account: ${error.message}` })
145
+ text: JSON.stringify({
146
+ error: `Failed to select account: ${error.message}`
147
+ })
144
148
  }
145
149
  ]
146
150
  };
@@ -149,6 +153,7 @@ function registerAuthTools(server, authManager) {
149
153
  );
150
154
  server.tool(
151
155
  "remove-account",
156
+ "Remove a Microsoft account from the cache",
152
157
  {
153
158
  accountId: z.string().describe("The account ID to remove")
154
159
  },
@@ -179,7 +184,9 @@ function registerAuthTools(server, authManager) {
179
184
  content: [
180
185
  {
181
186
  type: "text",
182
- text: JSON.stringify({ error: `Failed to remove account: ${error.message}` })
187
+ text: JSON.stringify({
188
+ error: `Failed to remove account: ${error.message}`
189
+ })
183
190
  }
184
191
  ]
185
192
  };
package/dist/auth.js CHANGED
@@ -49,9 +49,6 @@ function buildScopesFromEndpoints(includeWorkAccountScopes = false) {
49
49
  });
50
50
  return Array.from(scopesSet);
51
51
  }
52
- function buildAllScopes() {
53
- return buildScopesFromEndpoints(true);
54
- }
55
52
  class AuthManager {
56
53
  constructor(config = DEFAULT_CONFIG, scopes = buildScopesFromEndpoints()) {
57
54
  logger.info(`And scopes are ${scopes.join(", ")}`, scopes);
@@ -324,45 +321,6 @@ class AuthManager {
324
321
  return false;
325
322
  }
326
323
  }
327
- async expandToWorkAccountScopes(hack) {
328
- try {
329
- logger.info("Expanding to work account scopes...");
330
- const allScopes = buildAllScopes();
331
- const deviceCodeRequest = {
332
- scopes: allScopes,
333
- deviceCodeCallback: (response2) => {
334
- const text = [
335
- "\n",
336
- "\u{1F504} This feature requires additional permissions (work account scopes)",
337
- "\n",
338
- response2.message,
339
- "\n"
340
- ].join("");
341
- if (hack) {
342
- hack(text + 'After login run the "verify login" command');
343
- } else {
344
- console.log(text);
345
- }
346
- logger.info("Work account scope expansion initiated");
347
- }
348
- };
349
- const response = await this.msalApp.acquireTokenByDeviceCode(deviceCodeRequest);
350
- logger.info("Work account scope expansion successful");
351
- this.accessToken = response?.accessToken || null;
352
- this.tokenExpiry = response?.expiresOn ? new Date(response.expiresOn).getTime() : null;
353
- this.scopes = allScopes;
354
- if (response?.account) {
355
- this.selectedAccountId = response.account.homeAccountId;
356
- await this.saveSelectedAccount();
357
- logger.info(`Updated selected account after scope expansion: ${response.account.username}`);
358
- }
359
- await this.saveTokenCache();
360
- return true;
361
- } catch (error) {
362
- logger.error(`Error expanding to work account scopes: ${error.message}`);
363
- return false;
364
- }
365
- }
366
324
  // Multi-account support methods
367
325
  async listAccounts() {
368
326
  return await this.msalApp.getTokenCache().getAllAccounts();
@@ -431,7 +389,6 @@ class AuthManager {
431
389
  }
432
390
  var auth_default = AuthManager;
433
391
  export {
434
- buildAllScopes,
435
392
  buildScopesFromEndpoints,
436
393
  auth_default as default
437
394
  };
package/dist/cli.js CHANGED
@@ -17,9 +17,9 @@ program.name("ms-365-mcp-server").description("Microsoft 365 MCP Server").versio
17
17
  "--enabled-tools <pattern>",
18
18
  'Filter tools using regex pattern (e.g., "excel|contact" to enable Excel and Contact tools)'
19
19
  ).option(
20
- "--force-work-scopes",
21
- "Force inclusion of work account scopes during login (includes Teams, SharePoint, etc.)"
22
- );
20
+ "--org-mode",
21
+ "Enable organization/work mode from start (includes Teams, SharePoint, etc.)"
22
+ ).option("--work-mode", "Alias for --org-mode").option("--force-work-scopes", "Backwards compatibility alias for --org-mode (deprecated)");
23
23
  function parseArgs() {
24
24
  program.parse();
25
25
  const options = program.opts();
@@ -29,9 +29,15 @@ function parseArgs() {
29
29
  if (process.env.ENABLED_TOOLS) {
30
30
  options.enabledTools = process.env.ENABLED_TOOLS;
31
31
  }
32
+ if (process.env.MS365_MCP_ORG_MODE === "true" || process.env.MS365_MCP_ORG_MODE === "1") {
33
+ options.orgMode = true;
34
+ }
32
35
  if (process.env.MS365_MCP_FORCE_WORK_SCOPES === "true" || process.env.MS365_MCP_FORCE_WORK_SCOPES === "1") {
33
36
  options.forceWorkScopes = true;
34
37
  }
38
+ if (options.workMode || options.forceWorkScopes) {
39
+ options.orgMode = true;
40
+ }
35
41
  return options;
36
42
  }
37
43
  export {
@@ -3416,6 +3416,7 @@ const endpoints = makeApi([
3416
3416
  method: "post",
3417
3417
  path: "/chats/:chatId/messages/:chatMessageId/replies",
3418
3418
  alias: "reply-to-chat-message",
3419
+ description: `Create new navigation property to replies for chats`,
3419
3420
  requestFormat: "json",
3420
3421
  parameters: [
3421
3422
  {
@@ -3448,6 +3449,7 @@ const endpoints = makeApi([
3448
3449
  method: "delete",
3449
3450
  path: "/drives/:driveId/items/:driveItemId",
3450
3451
  alias: "delete-onedrive-file",
3452
+ description: `Delete navigation property items for drives`,
3451
3453
  requestFormat: "json",
3452
3454
  parameters: [
3453
3455
  {
@@ -3690,6 +3692,7 @@ const endpoints = makeApi([
3690
3692
  method: "patch",
3691
3693
  path: "/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range()/format",
3692
3694
  alias: "format-excel-range",
3695
+ description: `Update the navigation property format in drives`,
3693
3696
  requestFormat: "json",
3694
3697
  parameters: [
3695
3698
  {
@@ -3722,6 +3725,7 @@ const endpoints = makeApi([
3722
3725
  method: "patch",
3723
3726
  path: "/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range()/sort",
3724
3727
  alias: "sort-excel-range",
3728
+ description: `Update the navigation property sort in drives`,
3725
3729
  requestFormat: "json",
3726
3730
  parameters: [
3727
3731
  {
@@ -3758,6 +3762,7 @@ const endpoints = makeApi([
3758
3762
  method: "get",
3759
3763
  path: "/drives/:driveId/items/:driveItemId/workbook/worksheets/:workbookWorksheetId/range(address&#x3D;&#x27;:address&#x27;)",
3760
3764
  alias: "get-excel-range",
3765
+ description: `Invoke function range`,
3761
3766
  requestFormat: "json",
3762
3767
  response: z.void(),
3763
3768
  errors: [
@@ -4076,6 +4081,7 @@ or from some other calendar of the user.`,
4076
4081
  method: "get",
4077
4082
  path: "/me/chats",
4078
4083
  alias: "list-chats",
4084
+ description: `Get chats from me`,
4079
4085
  requestFormat: "json",
4080
4086
  parameters: [
4081
4087
  {
@@ -6573,6 +6579,7 @@ A site resource represents a team site in SharePoint.`,
6573
6579
  method: "get",
6574
6580
  path: "/sites/:siteId/getByPath(path&#x3D;&#x27;:path&#x27;)",
6575
6581
  alias: "get-sharepoint-site-by-path",
6582
+ description: `Invoke function getByPath`,
6576
6583
  requestFormat: "json",
6577
6584
  response: z.void(),
6578
6585
  errors: [
@@ -90,21 +90,12 @@ class GraphClient {
90
90
  if (response.status === 403) {
91
91
  const errorText = await response.text();
92
92
  if (errorText.includes("scope") || errorText.includes("permission")) {
93
- const hasWorkPermissions = await this.authManager.hasWorkAccountPermissions();
94
- if (!hasWorkPermissions) {
95
- logger.info("403 scope error detected, attempting to expand to work account scopes...");
96
- const expanded = await this.authManager.expandToWorkAccountScopes();
97
- if (expanded) {
98
- const newToken = await this.authManager.getToken();
99
- if (newToken) {
100
- logger.info("Retrying request with expanded scopes...");
101
- return this.performRequest(endpoint, newToken, options);
102
- }
103
- }
104
- }
93
+ throw new Error(
94
+ `Microsoft Graph API scope error: ${response.status} ${response.statusText} - ${errorText}. This tool requires organization mode. Please restart with --org-mode flag.`
95
+ );
105
96
  }
106
97
  throw new Error(
107
- `Microsoft Graph API scope error: ${response.status} ${response.statusText} - ${errorText}`
98
+ `Microsoft Graph API error: ${response.status} ${response.statusText} - ${errorText}`
108
99
  );
109
100
  }
110
101
  if (!response.ok) {
@@ -1,7 +1,15 @@
1
1
  import logger from "./logger.js";
2
2
  import { api } from "./generated/client.js";
3
3
  import { z } from "zod";
4
- function registerGraphTools(server, graphClient, readOnly = false, enabledToolsPattern) {
4
+ import { readFileSync } from "fs";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const endpointsData = JSON.parse(
10
+ readFileSync(path.join(__dirname, "endpoints.json"), "utf8")
11
+ );
12
+ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsPattern, orgMode = false) {
5
13
  let enabledToolsRegex;
6
14
  if (enabledToolsPattern) {
7
15
  try {
@@ -12,6 +20,11 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
12
20
  }
13
21
  }
14
22
  for (const tool of api.endpoints) {
23
+ const endpointConfig = endpointsData.find((e) => e.toolName === tool.alias);
24
+ if (endpointConfig?.requiresWorkAccount && !orgMode) {
25
+ logger.info(`Skipping work account tool ${tool.alias} - not in org mode`);
26
+ continue;
27
+ }
15
28
  if (readOnly && tool.method.toUpperCase() !== "GET") {
16
29
  logger.info(`Skipping write operation ${tool.alias} in read-only mode`);
17
30
  continue;
@@ -35,7 +48,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
35
48
  }
36
49
  server.tool(
37
50
  tool.alias,
38
- tool.description ?? "",
51
+ tool.description || `Execute ${tool.method.toUpperCase()} request to ${tool.path}`,
39
52
  paramSchema,
40
53
  {
41
54
  title: tool.alias,
@@ -46,7 +59,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
46
59
  try {
47
60
  logger.info(`params: ${JSON.stringify(params)}`);
48
61
  const parameterDefinitions = tool.parameters || [];
49
- let path = tool.path;
62
+ let path2 = tool.path;
50
63
  const queryParams = {};
51
64
  const headers = {};
52
65
  let body = null;
@@ -70,7 +83,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
70
83
  if (paramDef) {
71
84
  switch (paramDef.type) {
72
85
  case "Path":
73
- path = path.replace(`{${paramName}}`, encodeURIComponent(paramValue)).replace(`:${paramName}`, encodeURIComponent(paramValue));
86
+ path2 = path2.replace(`{${paramName}}`, encodeURIComponent(paramValue)).replace(`:${paramName}`, encodeURIComponent(paramValue));
74
87
  break;
75
88
  case "Query":
76
89
  queryParams[fixedParamName] = `${paramValue}`;
@@ -105,7 +118,7 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
105
118
  }
106
119
  if (Object.keys(queryParams).length > 0) {
107
120
  const queryString = Object.entries(queryParams).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
108
- path = `${path}${path.includes("?") ? "&" : "?"}${queryString}`;
121
+ path2 = `${path2}${path2.includes("?") ? "&" : "?"}${queryString}`;
109
122
  }
110
123
  const options = {
111
124
  method: tool.method.toUpperCase(),
@@ -114,12 +127,12 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
114
127
  if (options.method !== "GET" && body) {
115
128
  options.body = typeof body === "string" ? body : JSON.stringify(body);
116
129
  }
117
- const isProbablyMediaContent = tool.errors?.some((error) => error.description === "Retrieved media content") || path.endsWith("/content");
130
+ const isProbablyMediaContent = tool.errors?.some((error) => error.description === "Retrieved media content") || path2.endsWith("/content");
118
131
  if (isProbablyMediaContent) {
119
132
  options.rawResponse = true;
120
133
  }
121
- logger.info(`Making graph request to ${path} with options: ${JSON.stringify(options)}`);
122
- let response = await graphClient.graphRequest(path, options);
134
+ logger.info(`Making graph request to ${path2} with options: ${JSON.stringify(options)}`);
135
+ let response = await graphClient.graphRequest(path2, options);
123
136
  const fetchAllPages = params.fetchAllPages === true;
124
137
  if (fetchAllPages && response && response.content && response.content.length > 0) {
125
138
  try {
package/dist/index.js CHANGED
@@ -2,22 +2,15 @@
2
2
  import "dotenv/config";
3
3
  import { parseArgs } from "./cli.js";
4
4
  import logger from "./logger.js";
5
- import AuthManager from "./auth.js";
5
+ import AuthManager, { buildScopesFromEndpoints } from "./auth.js";
6
6
  import MicrosoftGraphServer from "./server.js";
7
7
  import { version } from "./version.js";
8
- import { buildScopesFromEndpoints } from "./auth.js";
9
8
  async function main() {
10
9
  try {
11
10
  const args = parseArgs();
12
- let includeWorkScopes = args.forceWorkScopes;
13
- if (!includeWorkScopes) {
14
- const tempAuthManager = new AuthManager(void 0, buildScopesFromEndpoints(false));
15
- await tempAuthManager.loadTokenCache();
16
- const hasWorkPermissions = await tempAuthManager.hasWorkAccountPermissions();
17
- if (hasWorkPermissions) {
18
- includeWorkScopes = true;
19
- logger.info("Detected existing work account permissions, including work scopes");
20
- }
11
+ const includeWorkScopes = args.orgMode || false;
12
+ if (includeWorkScopes) {
13
+ logger.info("Organization mode enabled - including work account scopes");
21
14
  }
22
15
  const scopes = buildScopesFromEndpoints(includeWorkScopes);
23
16
  const authManager = new AuthManager(void 0, scopes);
package/dist/server.js CHANGED
@@ -9,7 +9,11 @@ import { registerAuthTools } from "./auth-tools.js";
9
9
  import { registerGraphTools } from "./graph-tools.js";
10
10
  import GraphClient from "./graph-client.js";
11
11
  import { MicrosoftOAuthProvider } from "./oauth-provider.js";
12
- import { microsoftBearerTokenAuthMiddleware, exchangeCodeForToken, refreshAccessToken } from "./lib/microsoft-auth.js";
12
+ import {
13
+ exchangeCodeForToken,
14
+ microsoftBearerTokenAuthMiddleware,
15
+ refreshAccessToken
16
+ } from "./lib/microsoft-auth.js";
13
17
  const registeredClients = /* @__PURE__ */ new Map();
14
18
  class MicrosoftGraphServer {
15
19
  constructor(authManager, options = {}) {
@@ -31,7 +35,8 @@ class MicrosoftGraphServer {
31
35
  this.server,
32
36
  this.graphClient,
33
37
  this.options.readOnly,
34
- this.options.enabledTools
38
+ this.options.enabledTools,
39
+ this.options.orgMode
35
40
  );
36
41
  }
37
42
  async start() {
@@ -56,7 +61,10 @@ class MicrosoftGraphServer {
56
61
  app.use((req, res, next) => {
57
62
  res.header("Access-Control-Allow-Origin", "*");
58
63
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
59
- res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, mcp-protocol-version");
64
+ res.header(
65
+ "Access-Control-Allow-Headers",
66
+ "Origin, X-Requested-With, Content-Type, Accept, Authorization, mcp-protocol-version"
67
+ );
60
68
  if (req.method === "OPTIONS") {
61
69
  res.sendStatus(200);
62
70
  return;
@@ -76,11 +84,7 @@ class MicrosoftGraphServer {
76
84
  grant_types_supported: ["authorization_code", "refresh_token"],
77
85
  token_endpoint_auth_methods_supported: ["none"],
78
86
  code_challenge_methods_supported: ["S256"],
79
- scopes_supported: [
80
- "User.Read",
81
- "Files.Read",
82
- "Mail.Read"
83
- ]
87
+ scopes_supported: ["User.Read", "Files.Read", "Mail.Read"]
84
88
  });
85
89
  });
86
90
  app.get("/.well-known/oauth-protected-resource", async (req, res) => {
@@ -88,11 +92,7 @@ class MicrosoftGraphServer {
88
92
  res.json({
89
93
  resource: `${url.origin}/mcp`,
90
94
  authorization_servers: [url.origin],
91
- scopes_supported: [
92
- "User.Read",
93
- "Files.Read",
94
- "Mail.Read"
95
- ],
95
+ scopes_supported: ["User.Read", "Files.Read", "Mail.Read"],
96
96
  bearer_methods_supported: ["header"],
97
97
  resource_documentation: `${url.origin}`
98
98
  });
@@ -124,7 +124,9 @@ class MicrosoftGraphServer {
124
124
  const url = new URL(req.url, `${req.protocol}://${req.get("host")}`);
125
125
  const tenantId = process.env.MS365_MCP_TENANT_ID || "common";
126
126
  const clientId = process.env.MS365_MCP_CLIENT_ID || "084a3e9f-a9f4-43f7-89f9-d229cf97853e";
127
- const microsoftAuthUrl = new URL(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`);
127
+ const microsoftAuthUrl = new URL(
128
+ `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`
129
+ );
128
130
  const allowedParams = [
129
131
  "response_type",
130
132
  "redirect_uri",
@@ -275,7 +277,9 @@ class MicrosoftGraphServer {
275
277
  logger.info(`Server listening on HTTP port ${port}`);
276
278
  logger.info(` - MCP endpoint: http://localhost:${port}/mcp`);
277
279
  logger.info(` - OAuth endpoints: http://localhost:${port}/auth/*`);
278
- logger.info(` - OAuth discovery: http://localhost:${port}/.well-known/oauth-authorization-server`);
280
+ logger.info(
281
+ ` - OAuth discovery: http://localhost:${port}/.well-known/oauth-authorization-server`
282
+ );
279
283
  });
280
284
  } else {
281
285
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
4
4
  "description": "Microsoft 365 MCP Server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",