@softeria/ms-365-mcp-server 0.63.1 → 0.64.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/dist/endpoints.json +14 -0
- package/dist/generated/client.js +46 -0
- package/dist/server.js +48 -3
- package/logs/mcp-server.log +10 -10
- package/package.json +1 -1
- package/src/endpoints.json +14 -0
package/dist/endpoints.json
CHANGED
|
@@ -133,6 +133,13 @@
|
|
|
133
133
|
"scopes": ["Mail.ReadWrite"],
|
|
134
134
|
"llmTip": "Max 3MB. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}."
|
|
135
135
|
},
|
|
136
|
+
{
|
|
137
|
+
"pathPattern": "/me/messages/{message-id}/attachments/createUploadSession",
|
|
138
|
+
"method": "post",
|
|
139
|
+
"toolName": "create-mail-attachment-upload-session",
|
|
140
|
+
"scopes": ["Mail.ReadWrite"],
|
|
141
|
+
"llmTip": "For large attachments (3-150MB). Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl for direct PUT of file bytes."
|
|
142
|
+
},
|
|
136
143
|
{
|
|
137
144
|
"pathPattern": "/me/messages/{message-id}/attachments",
|
|
138
145
|
"method": "get",
|
|
@@ -428,6 +435,13 @@
|
|
|
428
435
|
"scopes": ["Files.ReadWrite"],
|
|
429
436
|
"llmTip": "Max 4MB. For new files use path format: /items/root:/path/to/file.txt:/content. Overwrites existing files without warning."
|
|
430
437
|
},
|
|
438
|
+
{
|
|
439
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/createUploadSession",
|
|
440
|
+
"method": "post",
|
|
441
|
+
"toolName": "create-upload-session",
|
|
442
|
+
"scopes": ["Files.ReadWrite"],
|
|
443
|
+
"llmTip": "For large file uploads (no size limit). Returns a pre-authenticated uploadUrl for direct PUT of file bytes. For new files use path: /items/{parentId}:/{fileName}:/createUploadSession. Body (optional): { item: { '@microsoft.graph.conflictBehavior': 'rename' } }."
|
|
444
|
+
},
|
|
431
445
|
{
|
|
432
446
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}",
|
|
433
447
|
"method": "patch",
|
package/dist/generated/client.js
CHANGED
|
@@ -748,6 +748,18 @@ const microsoft_graph_driveItemCollectionResponse = z.object({
|
|
|
748
748
|
"@odata.nextLink": z.string().nullable(),
|
|
749
749
|
value: z.array(microsoft_graph_driveItem)
|
|
750
750
|
}).partial().passthrough();
|
|
751
|
+
const create_upload_session_Body = z.object({ item: z.object({}).partial().passthrough() }).partial().passthrough();
|
|
752
|
+
const microsoft_graph_uploadSession = z.object({
|
|
753
|
+
expirationDateTime: z.string().regex(
|
|
754
|
+
/^[0-9]{4,}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]([.][0-9]{1,12})?(Z|[+-][0-9][0-9]:[0-9][0-9])$/
|
|
755
|
+
).datetime({ offset: true }).describe(
|
|
756
|
+
"The date and time in UTC that the upload session expires. The complete file must be uploaded before this expiration time is reached. Each fragment uploaded during the session extends the expiration time."
|
|
757
|
+
).nullish(),
|
|
758
|
+
nextExpectedRanges: z.array(z.string().nullable()).describe(
|
|
759
|
+
"A collection of byte ranges that the server is missing for the file. These ranges are zero indexed and of the format 'start-end' (for example '0-26' to indicate the first 27 bytes of the file). When uploading files as Outlook attachments, instead of a collection of ranges, this property always indicates a single value '{start}', the location in the file where the next upload should begin."
|
|
760
|
+
).optional(),
|
|
761
|
+
uploadUrl: z.string().describe("The URL endpoint that accepts PUT requests for byte ranges of the file.").nullish()
|
|
762
|
+
}).passthrough();
|
|
751
763
|
const microsoft_graph_workbookIcon = z.object({
|
|
752
764
|
index: z.number().gte(-2147483648).lte(2147483647).describe("The index of the icon in the given set.").optional(),
|
|
753
765
|
set: z.string().describe(
|
|
@@ -3865,6 +3877,8 @@ const schemas = {
|
|
|
3865
3877
|
microsoft_graph_pendingOperations,
|
|
3866
3878
|
microsoft_graph_driveItem,
|
|
3867
3879
|
microsoft_graph_driveItemCollectionResponse,
|
|
3880
|
+
create_upload_session_Body,
|
|
3881
|
+
microsoft_graph_uploadSession,
|
|
3868
3882
|
microsoft_graph_workbookIcon,
|
|
3869
3883
|
microsoft_graph_workbookFilterCriteria,
|
|
3870
3884
|
microsoft_graph_workbookFilter,
|
|
@@ -4604,6 +4618,22 @@ const endpoints = makeApi([
|
|
|
4604
4618
|
],
|
|
4605
4619
|
response: z.void()
|
|
4606
4620
|
},
|
|
4621
|
+
{
|
|
4622
|
+
method: "post",
|
|
4623
|
+
path: "/drives/:driveId/items/:driveItemId/createUploadSession",
|
|
4624
|
+
alias: "create-upload-session",
|
|
4625
|
+
description: `Invoke action createUploadSession`,
|
|
4626
|
+
requestFormat: "json",
|
|
4627
|
+
parameters: [
|
|
4628
|
+
{
|
|
4629
|
+
name: "body",
|
|
4630
|
+
description: `Action parameters`,
|
|
4631
|
+
type: "Body",
|
|
4632
|
+
schema: create_upload_session_Body
|
|
4633
|
+
}
|
|
4634
|
+
],
|
|
4635
|
+
response: z.void()
|
|
4636
|
+
},
|
|
4607
4637
|
{
|
|
4608
4638
|
method: "get",
|
|
4609
4639
|
path: "/drives/:driveId/items/:driveItemId/workbook/tables",
|
|
@@ -6927,6 +6957,22 @@ resource.`,
|
|
|
6927
6957
|
],
|
|
6928
6958
|
response: z.void()
|
|
6929
6959
|
},
|
|
6960
|
+
{
|
|
6961
|
+
method: "post",
|
|
6962
|
+
path: "/me/messages/:messageId/attachments/createUploadSession",
|
|
6963
|
+
alias: "create-mail-attachment-upload-session",
|
|
6964
|
+
description: `Create an upload session that allows an app to iteratively upload ranges of a file, so as to attach the file to the specified Outlook item. The item can be a message or event. Use this approach to attach a file if the file size is between 3 MB and 150 MB. To attach a file that's smaller than 3 MB, do a POST operation on the attachments navigation property of the Outlook item; see how to do this for a message or for an event. As part of the response, this action returns an upload URL that you can use in subsequent sequential PUT queries. Request headers for each PUT operation let you specify the exact range of bytes to be uploaded. This allows transfer to be resumed, in case the network connection is dropped during upload. The following are the steps to attach a file to an Outlook item using an upload session: See attach large files to Outlook messages or events for an example.`,
|
|
6965
|
+
requestFormat: "json",
|
|
6966
|
+
parameters: [
|
|
6967
|
+
{
|
|
6968
|
+
name: "body",
|
|
6969
|
+
description: `Action parameters`,
|
|
6970
|
+
type: "Body",
|
|
6971
|
+
schema: z.object({ AttachmentItem: z.object({}).partial().passthrough() }).partial().passthrough()
|
|
6972
|
+
}
|
|
6973
|
+
],
|
|
6974
|
+
response: z.void()
|
|
6975
|
+
},
|
|
6930
6976
|
{
|
|
6931
6977
|
method: "post",
|
|
6932
6978
|
path: "/me/messages/:messageId/createForward",
|
package/dist/server.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import { getSecrets } from "./secrets.js";
|
|
18
18
|
import { getCloudEndpoints } from "./cloud-config.js";
|
|
19
19
|
import { requestContext } from "./request-context.js";
|
|
20
|
+
import crypto from "node:crypto";
|
|
20
21
|
function parseHttpOption(httpOption) {
|
|
21
22
|
if (typeof httpOption === "boolean") {
|
|
22
23
|
return { host: void 0, port: 3e3 };
|
|
@@ -36,6 +37,8 @@ class MicrosoftGraphServer {
|
|
|
36
37
|
this.version = "0.0.0";
|
|
37
38
|
this.multiAccount = false;
|
|
38
39
|
this.accountNames = [];
|
|
40
|
+
// Two-leg PKCE: stores client's code_challenge and server's code_verifier, keyed by OAuth state
|
|
41
|
+
this.pkceStore = /* @__PURE__ */ new Map();
|
|
39
42
|
this.authManager = authManager;
|
|
40
43
|
this.options = options;
|
|
41
44
|
this.graphClient = null;
|
|
@@ -189,14 +192,15 @@ class MicrosoftGraphServer {
|
|
|
189
192
|
const microsoftAuthUrl = new URL(
|
|
190
193
|
`${cloudEndpoints.authority}/${tenantId}/oauth2/v2.0/authorize`
|
|
191
194
|
);
|
|
195
|
+
const clientCodeChallenge = url.searchParams.get("code_challenge");
|
|
196
|
+
const clientCodeChallengeMethod = url.searchParams.get("code_challenge_method");
|
|
197
|
+
const state = url.searchParams.get("state");
|
|
192
198
|
const allowedParams = [
|
|
193
199
|
"response_type",
|
|
194
200
|
"redirect_uri",
|
|
195
201
|
"scope",
|
|
196
202
|
"state",
|
|
197
203
|
"response_mode",
|
|
198
|
-
"code_challenge",
|
|
199
|
-
"code_challenge_method",
|
|
200
204
|
"prompt",
|
|
201
205
|
"login_hint",
|
|
202
206
|
"domain_hint"
|
|
@@ -207,6 +211,32 @@ class MicrosoftGraphServer {
|
|
|
207
211
|
microsoftAuthUrl.searchParams.set(param, value);
|
|
208
212
|
}
|
|
209
213
|
});
|
|
214
|
+
if (clientCodeChallenge && state) {
|
|
215
|
+
const serverCodeVerifier = crypto.randomBytes(32).toString("base64url");
|
|
216
|
+
const serverCodeChallenge = crypto.createHash("sha256").update(serverCodeVerifier).digest("base64url");
|
|
217
|
+
this.pkceStore.set(state, {
|
|
218
|
+
clientCodeChallenge,
|
|
219
|
+
clientCodeChallengeMethod: clientCodeChallengeMethod || "S256",
|
|
220
|
+
serverCodeVerifier,
|
|
221
|
+
createdAt: Date.now()
|
|
222
|
+
});
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
for (const [key, value] of this.pkceStore) {
|
|
225
|
+
if (now - value.createdAt > 10 * 60 * 1e3) {
|
|
226
|
+
this.pkceStore.delete(key);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
microsoftAuthUrl.searchParams.set("code_challenge", serverCodeChallenge);
|
|
230
|
+
microsoftAuthUrl.searchParams.set("code_challenge_method", "S256");
|
|
231
|
+
logger.info("Two-leg PKCE: stored client challenge, generated server challenge", {
|
|
232
|
+
state: state.substring(0, 8) + "..."
|
|
233
|
+
});
|
|
234
|
+
} else if (clientCodeChallenge) {
|
|
235
|
+
microsoftAuthUrl.searchParams.set("code_challenge", clientCodeChallenge);
|
|
236
|
+
if (clientCodeChallengeMethod) {
|
|
237
|
+
microsoftAuthUrl.searchParams.set("code_challenge_method", clientCodeChallengeMethod);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
210
240
|
microsoftAuthUrl.searchParams.set("client_id", clientId);
|
|
211
241
|
if (!microsoftAuthUrl.searchParams.get("scope")) {
|
|
212
242
|
microsoftAuthUrl.searchParams.set("scope", "User.Read Files.Read Mail.Read");
|
|
@@ -250,13 +280,28 @@ class MicrosoftGraphServer {
|
|
|
250
280
|
tenantId,
|
|
251
281
|
hasClientSecret: !!clientSecret
|
|
252
282
|
});
|
|
283
|
+
let serverCodeVerifier;
|
|
284
|
+
if (body.code_verifier) {
|
|
285
|
+
const clientVerifier = body.code_verifier;
|
|
286
|
+
const clientChallengeComputed = crypto.createHash("sha256").update(clientVerifier).digest("base64url");
|
|
287
|
+
for (const [state, pkceData] of this.pkceStore) {
|
|
288
|
+
if (pkceData.clientCodeChallenge === clientChallengeComputed) {
|
|
289
|
+
serverCodeVerifier = pkceData.serverCodeVerifier;
|
|
290
|
+
this.pkceStore.delete(state);
|
|
291
|
+
logger.info("Two-leg PKCE: matched client verifier, using server verifier", {
|
|
292
|
+
state: state.substring(0, 8) + "..."
|
|
293
|
+
});
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
253
298
|
const result = await exchangeCodeForToken(
|
|
254
299
|
body.code,
|
|
255
300
|
body.redirect_uri,
|
|
256
301
|
clientId,
|
|
257
302
|
clientSecret,
|
|
258
303
|
tenantId,
|
|
259
|
-
body.code_verifier,
|
|
304
|
+
serverCodeVerifier || body.code_verifier,
|
|
260
305
|
this.secrets.cloudType
|
|
261
306
|
);
|
|
262
307
|
res.json(result);
|
package/logs/mcp-server.log
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
2026-04-05
|
|
2
|
-
2026-04-05
|
|
3
|
-
2026-04-05
|
|
4
|
-
2026-04-05
|
|
5
|
-
2026-04-05
|
|
6
|
-
2026-04-05
|
|
7
|
-
2026-04-05
|
|
8
|
-
2026-04-05
|
|
9
|
-
2026-04-05
|
|
10
|
-
2026-04-05
|
|
1
|
+
2026-04-05 09:28:30 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
2
|
+
2026-04-05 09:28:30 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
3
|
+
2026-04-05 09:28:30 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
4
|
+
2026-04-05 09:28:30 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/messages
|
|
5
|
+
2026-04-05 09:28:30 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/calendar
|
|
6
|
+
2026-04-05 09:28:32 INFO: Using environment variables for secrets
|
|
7
|
+
2026-04-05 09:28:32 INFO: Using environment variables for secrets
|
|
8
|
+
2026-04-05 09:28:32 INFO: Using environment variables for secrets
|
|
9
|
+
2026-04-05 09:28:32 INFO: Using environment variables for secrets
|
|
10
|
+
2026-04-05 09:28:32 INFO: Using environment variables for secrets
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/src/endpoints.json
CHANGED
|
@@ -133,6 +133,13 @@
|
|
|
133
133
|
"scopes": ["Mail.ReadWrite"],
|
|
134
134
|
"llmTip": "Max 3MB. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}."
|
|
135
135
|
},
|
|
136
|
+
{
|
|
137
|
+
"pathPattern": "/me/messages/{message-id}/attachments/createUploadSession",
|
|
138
|
+
"method": "post",
|
|
139
|
+
"toolName": "create-mail-attachment-upload-session",
|
|
140
|
+
"scopes": ["Mail.ReadWrite"],
|
|
141
|
+
"llmTip": "For large attachments (3-150MB). Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl for direct PUT of file bytes."
|
|
142
|
+
},
|
|
136
143
|
{
|
|
137
144
|
"pathPattern": "/me/messages/{message-id}/attachments",
|
|
138
145
|
"method": "get",
|
|
@@ -428,6 +435,13 @@
|
|
|
428
435
|
"scopes": ["Files.ReadWrite"],
|
|
429
436
|
"llmTip": "Max 4MB. For new files use path format: /items/root:/path/to/file.txt:/content. Overwrites existing files without warning."
|
|
430
437
|
},
|
|
438
|
+
{
|
|
439
|
+
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}/createUploadSession",
|
|
440
|
+
"method": "post",
|
|
441
|
+
"toolName": "create-upload-session",
|
|
442
|
+
"scopes": ["Files.ReadWrite"],
|
|
443
|
+
"llmTip": "For large file uploads (no size limit). Returns a pre-authenticated uploadUrl for direct PUT of file bytes. For new files use path: /items/{parentId}:/{fileName}:/createUploadSession. Body (optional): { item: { '@microsoft.graph.conflictBehavior': 'rename' } }."
|
|
444
|
+
},
|
|
431
445
|
{
|
|
432
446
|
"pathPattern": "/drives/{drive-id}/items/{driveItem-id}",
|
|
433
447
|
"method": "patch",
|