@softeria/ms-365-mcp-server 0.160.0 → 0.161.1
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/__tests__/graph-tools.test.js +60 -1
- package/dist/endpoints.json +75 -0
- package/dist/generated/client.js +273 -18
- package/dist/graph-tools.js +17 -5
- package/dist/lib/param-descriptions.js +1 -1
- package/package.json +1 -1
- package/src/endpoints.json +75 -0
|
@@ -458,9 +458,10 @@ describe("graph-tools", () => {
|
|
|
458
458
|
const server = createMockServer();
|
|
459
459
|
const { registerGraphTools } = await loadModule();
|
|
460
460
|
registerGraphTools(server, graphClient);
|
|
461
|
-
await server.tools.get("test-tool").handler({ fetchAllPages: true });
|
|
461
|
+
const result = await server.tools.get("test-tool").handler({ fetchAllPages: true });
|
|
462
462
|
const [payload] = auditLogMock.mock.calls[0];
|
|
463
463
|
expect(payload.result_count).toBe(2);
|
|
464
|
+
expect(JSON.parse(result.content[0].text)["@odata.nextLink"]).toBeUndefined();
|
|
464
465
|
expect(payload.result_has_more).toBe(true);
|
|
465
466
|
expect(payload.response_bytes).toBe(100);
|
|
466
467
|
} finally {
|
|
@@ -471,6 +472,64 @@ describe("graph-tools", () => {
|
|
|
471
472
|
}
|
|
472
473
|
}
|
|
473
474
|
});
|
|
475
|
+
it("reports result_has_more when a page is not a collection", async () => {
|
|
476
|
+
mockEndpoints.push(makeEndpoint());
|
|
477
|
+
mockEndpointsJson = [makeConfig()];
|
|
478
|
+
const pageThreeLink = "https://graph.microsoft.com/v1.0/me/messages?$skip=4";
|
|
479
|
+
const graphClient = createMockGraphClient([
|
|
480
|
+
{
|
|
481
|
+
content: [
|
|
482
|
+
{
|
|
483
|
+
type: "text",
|
|
484
|
+
text: JSON.stringify({
|
|
485
|
+
value: [{ id: "1" }, { id: "2" }],
|
|
486
|
+
"@odata.nextLink": "https://graph.microsoft.com/v1.0/me/messages?$skip=2"
|
|
487
|
+
})
|
|
488
|
+
}
|
|
489
|
+
],
|
|
490
|
+
_meta: { http_status: 200, result_count: 2, result_has_more: true, response_bytes: 1e3 }
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: "text",
|
|
496
|
+
text: JSON.stringify({
|
|
497
|
+
value: [{ id: "3" }, { id: "4" }],
|
|
498
|
+
"@odata.nextLink": pageThreeLink
|
|
499
|
+
})
|
|
500
|
+
}
|
|
501
|
+
],
|
|
502
|
+
_meta: { http_status: 200, result_count: 2, result_has_more: true, response_bytes: 900 }
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
content: [
|
|
506
|
+
{
|
|
507
|
+
type: "text",
|
|
508
|
+
text: JSON.stringify({
|
|
509
|
+
message: "OK!",
|
|
510
|
+
contentType: "application/json",
|
|
511
|
+
encoding: "base64",
|
|
512
|
+
contentLength: 9,
|
|
513
|
+
contentBytes: "eyJ2YWx1"
|
|
514
|
+
})
|
|
515
|
+
}
|
|
516
|
+
],
|
|
517
|
+
_meta: { http_status: 200, response_bytes: 700 }
|
|
518
|
+
}
|
|
519
|
+
]);
|
|
520
|
+
const server = createMockServer();
|
|
521
|
+
const { registerGraphTools } = await loadModule();
|
|
522
|
+
registerGraphTools(server, graphClient);
|
|
523
|
+
const result = await server.tools.get("test-tool").handler({ fetchAllPages: true });
|
|
524
|
+
expect(graphClient.graphRequest).toHaveBeenCalledTimes(3);
|
|
525
|
+
const body = JSON.parse(result.content[0].text);
|
|
526
|
+
expect(body.value.map((item) => item.id)).toEqual(["1", "2", "3", "4"]);
|
|
527
|
+
expect(body["@odata.nextLink"]).toBe(pageThreeLink);
|
|
528
|
+
const [payload] = auditLogMock.mock.calls[0];
|
|
529
|
+
expect(payload.result_count).toBe(4);
|
|
530
|
+
expect(payload.result_has_more).toBe(true);
|
|
531
|
+
expect(payload.response_bytes).toBe(1900);
|
|
532
|
+
});
|
|
474
533
|
it("omits the volume fields when the client supplied none", async () => {
|
|
475
534
|
const endpoint = makeEndpoint({ method: "get", path: "/me", alias: "get-current-user" });
|
|
476
535
|
const config = makeConfig({
|
package/dist/endpoints.json
CHANGED
|
@@ -197,6 +197,64 @@
|
|
|
197
197
|
"workScopes": ["Mail.Send.Shared"],
|
|
198
198
|
"llmTip": "Send an existing draft from a shared mailbox. No request body needed — just call with the message ID and the shared mailbox user-id. Draft must exist in the shared mailbox's Drafts folder. Requires Send As permission on the shared mailbox in Exchange."
|
|
199
199
|
},
|
|
200
|
+
{
|
|
201
|
+
"pathPattern": "/users/{user-id}/mailFolders",
|
|
202
|
+
"method": "get",
|
|
203
|
+
"toolName": "list-shared-mailbox-folders",
|
|
204
|
+
"descriptionOverride": "List the top-level mail folders in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
205
|
+
"presets": ["work"],
|
|
206
|
+
"workScopes": ["Mail.Read.Shared"]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"pathPattern": "/users/{user-id}/mailFolders/{mailFolder-id}/childFolders",
|
|
210
|
+
"method": "get",
|
|
211
|
+
"toolName": "list-shared-mailbox-child-folders",
|
|
212
|
+
"descriptionOverride": "List the child folders of a mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
213
|
+
"presets": ["work"],
|
|
214
|
+
"workScopes": ["Mail.Read.Shared"]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"pathPattern": "/users/{user-id}/mailFolders",
|
|
218
|
+
"method": "post",
|
|
219
|
+
"toolName": "create-shared-mailbox-folder",
|
|
220
|
+
"descriptionOverride": "Create a top-level mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
221
|
+
"presets": ["work"],
|
|
222
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"pathPattern": "/users/{user-id}/mailFolders/{mailFolder-id}/childFolders",
|
|
226
|
+
"method": "post",
|
|
227
|
+
"toolName": "create-shared-mailbox-child-folder",
|
|
228
|
+
"descriptionOverride": "Create a subfolder inside a mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
229
|
+
"presets": ["work"],
|
|
230
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments",
|
|
234
|
+
"method": "get",
|
|
235
|
+
"toolName": "list-shared-mailbox-attachments",
|
|
236
|
+
"descriptionOverride": "List the attachments on a message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
237
|
+
"presets": ["work"],
|
|
238
|
+
"workScopes": ["Mail.Read.Shared"],
|
|
239
|
+
"llmTip": "To download the bytes, call download-bytes or get-download-url with target=/users/{user-id}/messages/{message-id}/attachments/{attachment-id}/$value."
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments",
|
|
243
|
+
"method": "post",
|
|
244
|
+
"toolName": "add-shared-mailbox-attachment",
|
|
245
|
+
"descriptionOverride": "Add an attachment under 3MB to a draft message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
246
|
+
"presets": ["work"],
|
|
247
|
+
"workScopes": ["Mail.ReadWrite.Shared"],
|
|
248
|
+
"llmTip": "Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}. Graph has no working path for 3MB and above here: upload sessions return 403 in a shared or delegated mailbox."
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments/{attachment-id}",
|
|
252
|
+
"method": "delete",
|
|
253
|
+
"toolName": "delete-shared-mailbox-attachment",
|
|
254
|
+
"descriptionOverride": "Delete an attachment from a message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
255
|
+
"presets": ["work"],
|
|
256
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
257
|
+
},
|
|
200
258
|
{
|
|
201
259
|
"pathPattern": "/users",
|
|
202
260
|
"method": "get",
|
|
@@ -2266,6 +2324,7 @@
|
|
|
2266
2324
|
"pathPattern": "/groups/{group-id}/threads/{conversationThread-id}/reply",
|
|
2267
2325
|
"method": "post",
|
|
2268
2326
|
"toolName": "reply-to-group-thread",
|
|
2327
|
+
"descriptionOverride": "Reply to a conversation thread in a Microsoft 365 group, adding a new post to the thread. Use list-group-threads to obtain the thread ID.",
|
|
2269
2328
|
"presets": ["work"],
|
|
2270
2329
|
"workScopes": ["Group.ReadWrite.All"],
|
|
2271
2330
|
"llmTip": "Legacy — Microsoft recommends Teams channels instead of group threads."
|
|
@@ -2394,6 +2453,22 @@
|
|
|
2394
2453
|
"scopes": ["Calendars.ReadWrite"],
|
|
2395
2454
|
"llmTip": "Dismisses a triggered event reminder so it won't re-fire. No request body required. Pair with list-calendar-events or get-schedule to find active reminders."
|
|
2396
2455
|
},
|
|
2456
|
+
{
|
|
2457
|
+
"pathPattern": "/me/events/{event-id}/attachments",
|
|
2458
|
+
"method": "post",
|
|
2459
|
+
"toolName": "add-calendar-event-attachment",
|
|
2460
|
+
"presets": ["calendar", "outlook", "personal"],
|
|
2461
|
+
"scopes": ["Calendars.ReadWrite"],
|
|
2462
|
+
"llmTip": "Under 3MB only. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}. At 3MB and above use create-calendar-event-attachment-upload-session."
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
"pathPattern": "/me/events/{event-id}/attachments/createUploadSession",
|
|
2466
|
+
"method": "post",
|
|
2467
|
+
"toolName": "create-calendar-event-attachment-upload-session",
|
|
2468
|
+
"presets": ["calendar", "outlook", "personal"],
|
|
2469
|
+
"scopes": ["Calendars.ReadWrite"],
|
|
2470
|
+
"llmTip": "For attachments 3MB to 150MB; under 3MB use add-calendar-event-attachment. Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself in ranges up to 4MB."
|
|
2471
|
+
},
|
|
2397
2472
|
{
|
|
2398
2473
|
"pathPattern": "/me/events/delta()",
|
|
2399
2474
|
"method": "get",
|
package/dist/generated/client.js
CHANGED
|
@@ -2774,6 +2774,20 @@ const microsoft_graph_driveCollectionResponse = z.object({
|
|
|
2774
2774
|
"@odata.nextLink": z.string().nullable(),
|
|
2775
2775
|
value: z.array(microsoft_graph_drive)
|
|
2776
2776
|
}).partial().passthrough();
|
|
2777
|
+
const microsoft_graph_attachmentType = z.enum(["file", "item", "reference"]);
|
|
2778
|
+
const microsoft_graph_attachmentItem = z.object({
|
|
2779
|
+
attachmentType: microsoft_graph_attachmentType.optional(),
|
|
2780
|
+
contentId: z.string().describe(
|
|
2781
|
+
"The CID or Content-Id of the attachment for referencing for the in-line attachments using the <img src='cid:contentId'> tag in HTML messages. Optional."
|
|
2782
|
+
).nullish(),
|
|
2783
|
+
contentType: z.string().describe("The nature of the data in the attachment. Optional.").nullish(),
|
|
2784
|
+
isInline: z.boolean().describe("true if the attachment is an inline attachment; otherwise, false. Optional.").nullish(),
|
|
2785
|
+
name: z.string().describe(
|
|
2786
|
+
"The display name of the attachment. This can be a descriptive string and doesn't have to be the actual file name. Required."
|
|
2787
|
+
).nullish(),
|
|
2788
|
+
size: z.number().describe("The length of the attachment in bytes. Required.").nullish()
|
|
2789
|
+
}).passthrough();
|
|
2790
|
+
const create_calendar_event_attachment_upload_session_Body = z.object({ AttachmentItem: microsoft_graph_attachmentItem }).partial().passthrough();
|
|
2777
2791
|
const accept_calendar_event_Body = z.object({ SendResponse: z.boolean().nullable().default(false), Comment: z.string().nullable() }).partial().passthrough();
|
|
2778
2792
|
const decline_calendar_event_Body = z.object({
|
|
2779
2793
|
ProposedNewTime: z.union([microsoft_graph_timeSlot, z.object({}).partial().passthrough()]),
|
|
@@ -3299,20 +3313,6 @@ const microsoft_graph_attachmentCollectionResponse = z.object({
|
|
|
3299
3313
|
"@odata.nextLink": z.string().nullable(),
|
|
3300
3314
|
value: z.array(microsoft_graph_attachment)
|
|
3301
3315
|
}).partial().passthrough();
|
|
3302
|
-
const microsoft_graph_attachmentType = z.enum(["file", "item", "reference"]);
|
|
3303
|
-
const microsoft_graph_attachmentItem = z.object({
|
|
3304
|
-
attachmentType: microsoft_graph_attachmentType.optional(),
|
|
3305
|
-
contentId: z.string().describe(
|
|
3306
|
-
"The CID or Content-Id of the attachment for referencing for the in-line attachments using the <img src='cid:contentId'> tag in HTML messages. Optional."
|
|
3307
|
-
).nullish(),
|
|
3308
|
-
contentType: z.string().describe("The nature of the data in the attachment. Optional.").nullish(),
|
|
3309
|
-
isInline: z.boolean().describe("true if the attachment is an inline attachment; otherwise, false. Optional.").nullish(),
|
|
3310
|
-
name: z.string().describe(
|
|
3311
|
-
"The display name of the attachment. This can be a descriptive string and doesn't have to be the actual file name. Required."
|
|
3312
|
-
).nullish(),
|
|
3313
|
-
size: z.number().describe("The length of the attachment in bytes. Required.").nullish()
|
|
3314
|
-
}).passthrough();
|
|
3315
|
-
const create_mail_attachment_upload_session_Body = z.object({ AttachmentItem: microsoft_graph_attachmentItem }).partial().passthrough();
|
|
3316
3316
|
const create_forward_draft_Body = z.object({
|
|
3317
3317
|
ToRecipients: z.array(microsoft_graph_recipient),
|
|
3318
3318
|
Message: z.union([microsoft_graph_message, z.object({}).partial().passthrough()]),
|
|
@@ -5149,6 +5149,9 @@ const schemas = {
|
|
|
5149
5149
|
microsoft_graph_list,
|
|
5150
5150
|
microsoft_graph_drive,
|
|
5151
5151
|
microsoft_graph_driveCollectionResponse,
|
|
5152
|
+
microsoft_graph_attachmentType,
|
|
5153
|
+
microsoft_graph_attachmentItem,
|
|
5154
|
+
create_calendar_event_attachment_upload_session_Body,
|
|
5152
5155
|
accept_calendar_event_Body,
|
|
5153
5156
|
decline_calendar_event_Body,
|
|
5154
5157
|
forward_calendar_event_Body,
|
|
@@ -5200,9 +5203,6 @@ const schemas = {
|
|
|
5200
5203
|
microsoft_graph_messageRuleCollectionResponse,
|
|
5201
5204
|
microsoft_graph_messageCollectionResponse,
|
|
5202
5205
|
microsoft_graph_attachmentCollectionResponse,
|
|
5203
|
-
microsoft_graph_attachmentType,
|
|
5204
|
-
microsoft_graph_attachmentItem,
|
|
5205
|
-
create_mail_attachment_upload_session_Body,
|
|
5206
5206
|
create_forward_draft_Body,
|
|
5207
5207
|
create_reply_draft_Body,
|
|
5208
5208
|
microsoft_graph_attendeeBase,
|
|
@@ -9377,6 +9377,38 @@ open extensions or extended properties, and how to specify extended properties.`
|
|
|
9377
9377
|
],
|
|
9378
9378
|
response: z.void()
|
|
9379
9379
|
},
|
|
9380
|
+
{
|
|
9381
|
+
method: "post",
|
|
9382
|
+
path: "/me/events/:eventId/attachments",
|
|
9383
|
+
alias: "add-calendar-event-attachment",
|
|
9384
|
+
description: `Use this API to add an attachment to an existing event. This operation limits the size of the attachment you can add to under 3 MB. If an organizer adds an attachment to a meeting event, the organizer can subsequently update the event to send the attachment and update the event for each attendee as well.`,
|
|
9385
|
+
requestFormat: "json",
|
|
9386
|
+
parameters: [
|
|
9387
|
+
{
|
|
9388
|
+
name: "body",
|
|
9389
|
+
description: `New navigation property`,
|
|
9390
|
+
type: "Body",
|
|
9391
|
+
schema: microsoft_graph_attachment
|
|
9392
|
+
}
|
|
9393
|
+
],
|
|
9394
|
+
response: z.void()
|
|
9395
|
+
},
|
|
9396
|
+
{
|
|
9397
|
+
method: "post",
|
|
9398
|
+
path: "/me/events/:eventId/attachments/createUploadSession",
|
|
9399
|
+
alias: "create-calendar-event-attachment-upload-session",
|
|
9400
|
+
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.`,
|
|
9401
|
+
requestFormat: "json",
|
|
9402
|
+
parameters: [
|
|
9403
|
+
{
|
|
9404
|
+
name: "body",
|
|
9405
|
+
description: `Action parameters`,
|
|
9406
|
+
type: "Body",
|
|
9407
|
+
schema: create_calendar_event_attachment_upload_session_Body
|
|
9408
|
+
}
|
|
9409
|
+
],
|
|
9410
|
+
response: z.void()
|
|
9411
|
+
},
|
|
9380
9412
|
{
|
|
9381
9413
|
method: "post",
|
|
9382
9414
|
path: "/me/events/:eventId/cancel",
|
|
@@ -10591,7 +10623,7 @@ resource.`,
|
|
|
10591
10623
|
name: "body",
|
|
10592
10624
|
description: `Action parameters`,
|
|
10593
10625
|
type: "Body",
|
|
10594
|
-
schema:
|
|
10626
|
+
schema: create_calendar_event_attachment_upload_session_Body
|
|
10595
10627
|
}
|
|
10596
10628
|
],
|
|
10597
10629
|
response: z.void()
|
|
@@ -15013,6 +15045,148 @@ Only the policyViolation property of a chatMessage can be updated in application
|
|
|
15013
15045
|
],
|
|
15014
15046
|
response: z.void()
|
|
15015
15047
|
},
|
|
15048
|
+
{
|
|
15049
|
+
method: "get",
|
|
15050
|
+
path: "/users/:userId/mailFolders",
|
|
15051
|
+
alias: "list-shared-mailbox-folders",
|
|
15052
|
+
description: `The user's mail folders. Read-only. Nullable.`,
|
|
15053
|
+
requestFormat: "json",
|
|
15054
|
+
parameters: [
|
|
15055
|
+
{
|
|
15056
|
+
name: "includeHiddenFolders",
|
|
15057
|
+
type: "Query",
|
|
15058
|
+
schema: z.string().describe("Include Hidden Folders").optional()
|
|
15059
|
+
},
|
|
15060
|
+
{
|
|
15061
|
+
name: "$top",
|
|
15062
|
+
type: "Query",
|
|
15063
|
+
schema: z.number().int().gte(0).describe("Show only the first n items").optional()
|
|
15064
|
+
},
|
|
15065
|
+
{
|
|
15066
|
+
name: "$skip",
|
|
15067
|
+
type: "Query",
|
|
15068
|
+
schema: z.number().int().gte(0).describe("Skip the first n items").optional()
|
|
15069
|
+
},
|
|
15070
|
+
{
|
|
15071
|
+
name: "$search",
|
|
15072
|
+
type: "Query",
|
|
15073
|
+
schema: z.string().describe("Search items by search phrases").optional()
|
|
15074
|
+
},
|
|
15075
|
+
{
|
|
15076
|
+
name: "$filter",
|
|
15077
|
+
type: "Query",
|
|
15078
|
+
schema: z.string().describe("Filter items by property values").optional()
|
|
15079
|
+
},
|
|
15080
|
+
{
|
|
15081
|
+
name: "$count",
|
|
15082
|
+
type: "Query",
|
|
15083
|
+
schema: z.boolean().describe("Include count of items").optional()
|
|
15084
|
+
},
|
|
15085
|
+
{
|
|
15086
|
+
name: "$orderby",
|
|
15087
|
+
type: "Query",
|
|
15088
|
+
schema: z.array(z.string()).describe("Order items by property values").optional()
|
|
15089
|
+
},
|
|
15090
|
+
{
|
|
15091
|
+
name: "$select",
|
|
15092
|
+
type: "Query",
|
|
15093
|
+
schema: z.array(z.string()).describe("Select properties to be returned").optional()
|
|
15094
|
+
},
|
|
15095
|
+
{
|
|
15096
|
+
name: "$expand",
|
|
15097
|
+
type: "Query",
|
|
15098
|
+
schema: z.array(z.string()).describe("Expand related entities").optional()
|
|
15099
|
+
}
|
|
15100
|
+
],
|
|
15101
|
+
response: z.void()
|
|
15102
|
+
},
|
|
15103
|
+
{
|
|
15104
|
+
method: "post",
|
|
15105
|
+
path: "/users/:userId/mailFolders",
|
|
15106
|
+
alias: "create-shared-mailbox-folder",
|
|
15107
|
+
description: `Create a shared mailbox folder.`,
|
|
15108
|
+
requestFormat: "json",
|
|
15109
|
+
parameters: [
|
|
15110
|
+
{
|
|
15111
|
+
name: "body",
|
|
15112
|
+
description: `New navigation property`,
|
|
15113
|
+
type: "Body",
|
|
15114
|
+
schema: microsoft_graph_mailFolder
|
|
15115
|
+
}
|
|
15116
|
+
],
|
|
15117
|
+
response: z.void()
|
|
15118
|
+
},
|
|
15119
|
+
{
|
|
15120
|
+
method: "get",
|
|
15121
|
+
path: "/users/:userId/mailFolders/:mailFolderId/childFolders",
|
|
15122
|
+
alias: "list-shared-mailbox-child-folders",
|
|
15123
|
+
description: `The collection of child folders in the mailFolder.`,
|
|
15124
|
+
requestFormat: "json",
|
|
15125
|
+
parameters: [
|
|
15126
|
+
{
|
|
15127
|
+
name: "includeHiddenFolders",
|
|
15128
|
+
type: "Query",
|
|
15129
|
+
schema: z.string().describe("Include Hidden Folders").optional()
|
|
15130
|
+
},
|
|
15131
|
+
{
|
|
15132
|
+
name: "$top",
|
|
15133
|
+
type: "Query",
|
|
15134
|
+
schema: z.number().int().gte(0).describe("Show only the first n items").optional()
|
|
15135
|
+
},
|
|
15136
|
+
{
|
|
15137
|
+
name: "$skip",
|
|
15138
|
+
type: "Query",
|
|
15139
|
+
schema: z.number().int().gte(0).describe("Skip the first n items").optional()
|
|
15140
|
+
},
|
|
15141
|
+
{
|
|
15142
|
+
name: "$search",
|
|
15143
|
+
type: "Query",
|
|
15144
|
+
schema: z.string().describe("Search items by search phrases").optional()
|
|
15145
|
+
},
|
|
15146
|
+
{
|
|
15147
|
+
name: "$filter",
|
|
15148
|
+
type: "Query",
|
|
15149
|
+
schema: z.string().describe("Filter items by property values").optional()
|
|
15150
|
+
},
|
|
15151
|
+
{
|
|
15152
|
+
name: "$count",
|
|
15153
|
+
type: "Query",
|
|
15154
|
+
schema: z.boolean().describe("Include count of items").optional()
|
|
15155
|
+
},
|
|
15156
|
+
{
|
|
15157
|
+
name: "$orderby",
|
|
15158
|
+
type: "Query",
|
|
15159
|
+
schema: z.array(z.string()).describe("Order items by property values").optional()
|
|
15160
|
+
},
|
|
15161
|
+
{
|
|
15162
|
+
name: "$select",
|
|
15163
|
+
type: "Query",
|
|
15164
|
+
schema: z.array(z.string()).describe("Select properties to be returned").optional()
|
|
15165
|
+
},
|
|
15166
|
+
{
|
|
15167
|
+
name: "$expand",
|
|
15168
|
+
type: "Query",
|
|
15169
|
+
schema: z.array(z.string()).describe("Expand related entities").optional()
|
|
15170
|
+
}
|
|
15171
|
+
],
|
|
15172
|
+
response: z.void()
|
|
15173
|
+
},
|
|
15174
|
+
{
|
|
15175
|
+
method: "post",
|
|
15176
|
+
path: "/users/:userId/mailFolders/:mailFolderId/childFolders",
|
|
15177
|
+
alias: "create-shared-mailbox-child-folder",
|
|
15178
|
+
description: `Create a shared mailbox child folder.`,
|
|
15179
|
+
requestFormat: "json",
|
|
15180
|
+
parameters: [
|
|
15181
|
+
{
|
|
15182
|
+
name: "body",
|
|
15183
|
+
description: `New navigation property`,
|
|
15184
|
+
type: "Body",
|
|
15185
|
+
schema: microsoft_graph_mailFolder
|
|
15186
|
+
}
|
|
15187
|
+
],
|
|
15188
|
+
response: z.void()
|
|
15189
|
+
},
|
|
15016
15190
|
{
|
|
15017
15191
|
method: "get",
|
|
15018
15192
|
path: "/users/:userId/mailFolders/:mailFolderId/messages",
|
|
@@ -15294,6 +15468,87 @@ Only the policyViolation property of a chatMessage can be updated in application
|
|
|
15294
15468
|
],
|
|
15295
15469
|
response: z.void()
|
|
15296
15470
|
},
|
|
15471
|
+
{
|
|
15472
|
+
method: "get",
|
|
15473
|
+
path: "/users/:userId/messages/:messageId/attachments",
|
|
15474
|
+
alias: "list-shared-mailbox-attachments",
|
|
15475
|
+
description: `The fileAttachment and itemAttachment attachments for the message.`,
|
|
15476
|
+
requestFormat: "json",
|
|
15477
|
+
parameters: [
|
|
15478
|
+
{
|
|
15479
|
+
name: "$top",
|
|
15480
|
+
type: "Query",
|
|
15481
|
+
schema: z.number().int().gte(0).describe("Show only the first n items").optional()
|
|
15482
|
+
},
|
|
15483
|
+
{
|
|
15484
|
+
name: "$skip",
|
|
15485
|
+
type: "Query",
|
|
15486
|
+
schema: z.number().int().gte(0).describe("Skip the first n items").optional()
|
|
15487
|
+
},
|
|
15488
|
+
{
|
|
15489
|
+
name: "$search",
|
|
15490
|
+
type: "Query",
|
|
15491
|
+
schema: z.string().describe("Search items by search phrases").optional()
|
|
15492
|
+
},
|
|
15493
|
+
{
|
|
15494
|
+
name: "$filter",
|
|
15495
|
+
type: "Query",
|
|
15496
|
+
schema: z.string().describe("Filter items by property values").optional()
|
|
15497
|
+
},
|
|
15498
|
+
{
|
|
15499
|
+
name: "$count",
|
|
15500
|
+
type: "Query",
|
|
15501
|
+
schema: z.boolean().describe("Include count of items").optional()
|
|
15502
|
+
},
|
|
15503
|
+
{
|
|
15504
|
+
name: "$orderby",
|
|
15505
|
+
type: "Query",
|
|
15506
|
+
schema: z.array(z.string()).describe("Order items by property values").optional()
|
|
15507
|
+
},
|
|
15508
|
+
{
|
|
15509
|
+
name: "$select",
|
|
15510
|
+
type: "Query",
|
|
15511
|
+
schema: z.array(z.string()).describe("Select properties to be returned").optional()
|
|
15512
|
+
},
|
|
15513
|
+
{
|
|
15514
|
+
name: "$expand",
|
|
15515
|
+
type: "Query",
|
|
15516
|
+
schema: z.array(z.string()).describe("Expand related entities").optional()
|
|
15517
|
+
}
|
|
15518
|
+
],
|
|
15519
|
+
response: z.void()
|
|
15520
|
+
},
|
|
15521
|
+
{
|
|
15522
|
+
method: "post",
|
|
15523
|
+
path: "/users/:userId/messages/:messageId/attachments",
|
|
15524
|
+
alias: "add-shared-mailbox-attachment",
|
|
15525
|
+
description: `Add a shared mailbox attachment.`,
|
|
15526
|
+
requestFormat: "json",
|
|
15527
|
+
parameters: [
|
|
15528
|
+
{
|
|
15529
|
+
name: "body",
|
|
15530
|
+
description: `New navigation property`,
|
|
15531
|
+
type: "Body",
|
|
15532
|
+
schema: microsoft_graph_attachment
|
|
15533
|
+
}
|
|
15534
|
+
],
|
|
15535
|
+
response: z.void()
|
|
15536
|
+
},
|
|
15537
|
+
{
|
|
15538
|
+
method: "delete",
|
|
15539
|
+
path: "/users/:userId/messages/:messageId/attachments/:attachmentId",
|
|
15540
|
+
alias: "delete-shared-mailbox-attachment",
|
|
15541
|
+
description: `Delete a shared mailbox attachment.`,
|
|
15542
|
+
requestFormat: "json",
|
|
15543
|
+
parameters: [
|
|
15544
|
+
{
|
|
15545
|
+
name: "If-Match",
|
|
15546
|
+
type: "Header",
|
|
15547
|
+
schema: z.string().describe("ETag").optional()
|
|
15548
|
+
}
|
|
15549
|
+
],
|
|
15550
|
+
response: z.void()
|
|
15551
|
+
},
|
|
15297
15552
|
{
|
|
15298
15553
|
method: "post",
|
|
15299
15554
|
path: "/users/:userId/messages/:messageId/createForward",
|
package/dist/graph-tools.js
CHANGED
|
@@ -554,8 +554,9 @@ function uploadError(message) {
|
|
|
554
554
|
async function mintUploadUrl(target, name, contentType, accountParam, authManager) {
|
|
555
555
|
const minting = getAttachmentMinting();
|
|
556
556
|
if (!minting) {
|
|
557
|
+
const smallFileTool = target.includes("/events/") ? "add-calendar-event-attachment" : target.startsWith("/users/") ? "add-shared-mailbox-attachment" : "add-mail-attachment";
|
|
557
558
|
return uploadError(
|
|
558
|
-
|
|
559
|
+
`This server is not running with --enable-attachment-urls, so no upload URL can be minted. Attach small files with ${smallFileTool} (base64 contentBytes).`
|
|
559
560
|
);
|
|
560
561
|
}
|
|
561
562
|
if (!UPLOAD_TARGET.test(target) || !isPlainGraphPath(target)) {
|
|
@@ -1725,6 +1726,7 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1725
1726
|
const maxPages = positiveIntFromEnv("MS365_MCP_MAX_PAGES", DEFAULT_MAX_PAGES);
|
|
1726
1727
|
const maxItems = positiveIntFromEnv("MS365_MCP_MAX_ITEMS", DEFAULT_MAX_ITEMS);
|
|
1727
1728
|
let deltaLink = combinedResponse["@odata.deltaLink"];
|
|
1729
|
+
let unmergeablePage = false;
|
|
1728
1730
|
while (nextLink && pageCount < maxPages && allItems.length < maxItems) {
|
|
1729
1731
|
logger.info(`Fetching page ${pageCount + 1} from: ${nextLink}`);
|
|
1730
1732
|
const url = new URL(nextLink);
|
|
@@ -1738,9 +1740,14 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1738
1740
|
}
|
|
1739
1741
|
if (nextResponse?.content?.[0]?.text) {
|
|
1740
1742
|
const nextJsonResponse = JSON.parse(nextResponse.content[0].text);
|
|
1741
|
-
if (Array.isArray(nextJsonResponse.value)) {
|
|
1742
|
-
|
|
1743
|
+
if (!Array.isArray(nextJsonResponse.value)) {
|
|
1744
|
+
logger.warn(
|
|
1745
|
+
`Pagination stopped at page ${pageCount + 1}: response was not a collection`
|
|
1746
|
+
);
|
|
1747
|
+
unmergeablePage = true;
|
|
1748
|
+
break;
|
|
1743
1749
|
}
|
|
1750
|
+
allItems = allItems.concat(nextJsonResponse.value);
|
|
1744
1751
|
nextLink = nextJsonResponse["@odata.nextLink"];
|
|
1745
1752
|
if (typeof totalResponseBytes === "number" && typeof nextResponse._meta?.response_bytes === "number") {
|
|
1746
1753
|
totalResponseBytes += nextResponse._meta.response_bytes;
|
|
@@ -1750,6 +1757,7 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1750
1757
|
}
|
|
1751
1758
|
pageCount++;
|
|
1752
1759
|
} else {
|
|
1760
|
+
unmergeablePage = true;
|
|
1753
1761
|
break;
|
|
1754
1762
|
}
|
|
1755
1763
|
}
|
|
@@ -1766,7 +1774,11 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1766
1774
|
if (combinedResponse["@odata.count"]) {
|
|
1767
1775
|
combinedResponse["@odata.count"] = allItems.length;
|
|
1768
1776
|
}
|
|
1769
|
-
|
|
1777
|
+
if (unmergeablePage && nextLink) {
|
|
1778
|
+
combinedResponse["@odata.nextLink"] = nextLink;
|
|
1779
|
+
} else {
|
|
1780
|
+
delete combinedResponse["@odata.nextLink"];
|
|
1781
|
+
}
|
|
1770
1782
|
response._meta = {
|
|
1771
1783
|
...response._meta,
|
|
1772
1784
|
result_count: allItems.length,
|
|
@@ -1777,7 +1789,7 @@ async function executeGraphTool(tool, config, graphClient, params, authManager,
|
|
|
1777
1789
|
combinedResponse["@odata.deltaLink"] = deltaLink;
|
|
1778
1790
|
}
|
|
1779
1791
|
logger.info(
|
|
1780
|
-
`Pagination complete: collected ${allItems.length} items across ${pageCount} pages`
|
|
1792
|
+
`Pagination ${nextLink ? "incomplete" : "complete"}: collected ${allItems.length} items across ${pageCount} pages`
|
|
1781
1793
|
);
|
|
1782
1794
|
}
|
|
1783
1795
|
}
|
|
@@ -31,7 +31,7 @@ function isFetchAllPagesApplicable(tool) {
|
|
|
31
31
|
const FILTER_PARAM_DESCRIPTION = "OData filter expression. Add $count=true for advanced filters (flag/flagStatus, contains()). Cannot combine with $search.";
|
|
32
32
|
const SEARCH_PARAM_DESCRIPTION = "KQL search query in one pair of double quotes; directory (users/groups) instead quotes each clause with no outer pair. Cannot combine with $filter.";
|
|
33
33
|
const SELECT_PARAM_DESCRIPTION = "Comma-separated fields to return, e.g. id,subject,from,receivedDateTime";
|
|
34
|
-
const EXPAND_PARAM_DESCRIPTION = 'Navigation properties to inline, e.g. attachments on a message or event. Only navigation properties can be expanded: expanding a non-navigation property such as a message body fails with "Parsing OData Select and Expand failed"
|
|
34
|
+
const EXPAND_PARAM_DESCRIPTION = 'Navigation properties to inline, e.g. attachments on a message or event. Only navigation properties can be expanded: expanding a non-navigation property such as a message body fails with "Parsing OData Select and Expand failed". Request ordinary fields with $select instead. Some APIs accept only one relationship per request, so prefer a single value. Some relationships support nested selection, e.g. children($select=id,name).';
|
|
35
35
|
const ORDERBY_PARAM_DESCRIPTION = "Sort expression, e.g. receivedDateTime desc";
|
|
36
36
|
const TOP_PARAM_DESCRIPTION = "Page size (Graph $top). Start small (e.g. 5\u201315) so responses fit the model context; raise only if needed. Use $select to return fewer fields per item. For more rows, use @odata.nextLink from the response instead of a very large $top.";
|
|
37
37
|
const SKIP_PARAM_DESCRIPTION = "Items to skip for pagination. Not supported with $search.";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softeria/ms-365-mcp-server",
|
|
3
3
|
"mcpName": "io.github.Softeria/ms-365-mcp-server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.161.1",
|
|
5
5
|
"description": " A Model Context Protocol (MCP) server for interacting with Microsoft 365 and Office services through the Graph API",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
package/src/endpoints.json
CHANGED
|
@@ -197,6 +197,64 @@
|
|
|
197
197
|
"workScopes": ["Mail.Send.Shared"],
|
|
198
198
|
"llmTip": "Send an existing draft from a shared mailbox. No request body needed — just call with the message ID and the shared mailbox user-id. Draft must exist in the shared mailbox's Drafts folder. Requires Send As permission on the shared mailbox in Exchange."
|
|
199
199
|
},
|
|
200
|
+
{
|
|
201
|
+
"pathPattern": "/users/{user-id}/mailFolders",
|
|
202
|
+
"method": "get",
|
|
203
|
+
"toolName": "list-shared-mailbox-folders",
|
|
204
|
+
"descriptionOverride": "List the top-level mail folders in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
205
|
+
"presets": ["work"],
|
|
206
|
+
"workScopes": ["Mail.Read.Shared"]
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"pathPattern": "/users/{user-id}/mailFolders/{mailFolder-id}/childFolders",
|
|
210
|
+
"method": "get",
|
|
211
|
+
"toolName": "list-shared-mailbox-child-folders",
|
|
212
|
+
"descriptionOverride": "List the child folders of a mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
213
|
+
"presets": ["work"],
|
|
214
|
+
"workScopes": ["Mail.Read.Shared"]
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"pathPattern": "/users/{user-id}/mailFolders",
|
|
218
|
+
"method": "post",
|
|
219
|
+
"toolName": "create-shared-mailbox-folder",
|
|
220
|
+
"descriptionOverride": "Create a top-level mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
221
|
+
"presets": ["work"],
|
|
222
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"pathPattern": "/users/{user-id}/mailFolders/{mailFolder-id}/childFolders",
|
|
226
|
+
"method": "post",
|
|
227
|
+
"toolName": "create-shared-mailbox-child-folder",
|
|
228
|
+
"descriptionOverride": "Create a subfolder inside a mail folder in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
229
|
+
"presets": ["work"],
|
|
230
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments",
|
|
234
|
+
"method": "get",
|
|
235
|
+
"toolName": "list-shared-mailbox-attachments",
|
|
236
|
+
"descriptionOverride": "List the attachments on a message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
237
|
+
"presets": ["work"],
|
|
238
|
+
"workScopes": ["Mail.Read.Shared"],
|
|
239
|
+
"llmTip": "To download the bytes, call download-bytes or get-download-url with target=/users/{user-id}/messages/{message-id}/attachments/{attachment-id}/$value."
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments",
|
|
243
|
+
"method": "post",
|
|
244
|
+
"toolName": "add-shared-mailbox-attachment",
|
|
245
|
+
"descriptionOverride": "Add an attachment under 3MB to a draft message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
246
|
+
"presets": ["work"],
|
|
247
|
+
"workScopes": ["Mail.ReadWrite.Shared"],
|
|
248
|
+
"llmTip": "Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}. Graph has no working path for 3MB and above here: upload sessions return 403 in a shared or delegated mailbox."
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"pathPattern": "/users/{user-id}/messages/{message-id}/attachments/{attachment-id}",
|
|
252
|
+
"method": "delete",
|
|
253
|
+
"toolName": "delete-shared-mailbox-attachment",
|
|
254
|
+
"descriptionOverride": "Delete an attachment from a message in another user's mailbox or a shared mailbox. The user-id is the mailbox owner's ID or email address, and delegated access to that mailbox is required.",
|
|
255
|
+
"presets": ["work"],
|
|
256
|
+
"workScopes": ["Mail.ReadWrite.Shared"]
|
|
257
|
+
},
|
|
200
258
|
{
|
|
201
259
|
"pathPattern": "/users",
|
|
202
260
|
"method": "get",
|
|
@@ -2266,6 +2324,7 @@
|
|
|
2266
2324
|
"pathPattern": "/groups/{group-id}/threads/{conversationThread-id}/reply",
|
|
2267
2325
|
"method": "post",
|
|
2268
2326
|
"toolName": "reply-to-group-thread",
|
|
2327
|
+
"descriptionOverride": "Reply to a conversation thread in a Microsoft 365 group, adding a new post to the thread. Use list-group-threads to obtain the thread ID.",
|
|
2269
2328
|
"presets": ["work"],
|
|
2270
2329
|
"workScopes": ["Group.ReadWrite.All"],
|
|
2271
2330
|
"llmTip": "Legacy — Microsoft recommends Teams channels instead of group threads."
|
|
@@ -2394,6 +2453,22 @@
|
|
|
2394
2453
|
"scopes": ["Calendars.ReadWrite"],
|
|
2395
2454
|
"llmTip": "Dismisses a triggered event reminder so it won't re-fire. No request body required. Pair with list-calendar-events or get-schedule to find active reminders."
|
|
2396
2455
|
},
|
|
2456
|
+
{
|
|
2457
|
+
"pathPattern": "/me/events/{event-id}/attachments",
|
|
2458
|
+
"method": "post",
|
|
2459
|
+
"toolName": "add-calendar-event-attachment",
|
|
2460
|
+
"presets": ["calendar", "outlook", "personal"],
|
|
2461
|
+
"scopes": ["Calendars.ReadWrite"],
|
|
2462
|
+
"llmTip": "Under 3MB only. Body requires @odata.type: {\"@odata.type\": \"#microsoft.graph.fileAttachment\", \"name\": \"file.pdf\", \"contentBytes\": \"<base64>\"}. At 3MB and above use create-calendar-event-attachment-upload-session."
|
|
2463
|
+
},
|
|
2464
|
+
{
|
|
2465
|
+
"pathPattern": "/me/events/{event-id}/attachments/createUploadSession",
|
|
2466
|
+
"method": "post",
|
|
2467
|
+
"toolName": "create-calendar-event-attachment-upload-session",
|
|
2468
|
+
"presets": ["calendar", "outlook", "personal"],
|
|
2469
|
+
"scopes": ["Calendars.ReadWrite"],
|
|
2470
|
+
"llmTip": "For attachments 3MB to 150MB; under 3MB use add-calendar-event-attachment. Body: { AttachmentItem: { attachmentType: 'file', name: 'report.pdf', size: 5000000 } }. Returns a pre-authenticated uploadUrl; the caller PUTs the bytes there itself in ranges up to 4MB."
|
|
2471
|
+
},
|
|
2397
2472
|
{
|
|
2398
2473
|
"pathPattern": "/me/events/delta()",
|
|
2399
2474
|
"method": "get",
|