@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 +21 -15
- package/bin/modules/simplified-openapi.mjs +3 -0
- package/dist/auth-tools.js +12 -5
- package/dist/auth.js +0 -43
- package/dist/cli.js +9 -3
- package/dist/generated/client.js +7 -0
- package/dist/graph-client.js +4 -13
- package/dist/graph-tools.js +21 -8
- package/dist/index.js +4 -11
- package/dist/server.js +19 -15
- package/package.json +1 -1
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
|
|
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
|
-
**
|
|
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**
|
|
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
|
-
|
|
71
|
+
## Organization/Work Mode
|
|
65
72
|
|
|
66
|
-
|
|
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
|
-
"--
|
|
83
|
+
"--org-mode"
|
|
78
84
|
]
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
```
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
--
|
|
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
|
-
- `
|
|
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
|
}
|
package/dist/auth-tools.js
CHANGED
|
@@ -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({
|
|
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({
|
|
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
|
-
"--
|
|
21
|
-
"
|
|
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 {
|
package/dist/generated/client.js
CHANGED
|
@@ -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=':address')",
|
|
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=':path')",
|
|
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: [
|
package/dist/graph-client.js
CHANGED
|
@@ -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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
98
|
+
`Microsoft Graph API error: ${response.status} ${response.statusText} - ${errorText}`
|
|
108
99
|
);
|
|
109
100
|
}
|
|
110
101
|
if (!response.ok) {
|
package/dist/graph-tools.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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") ||
|
|
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 ${
|
|
122
|
-
let response = await graphClient.graphRequest(
|
|
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
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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();
|