@softeria/ms-365-mcp-server 0.45.1 → 0.46.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 +4 -3
- package/SECURITY.md +11 -0
- package/dist/endpoints.json +51 -2
- package/dist/generated/client.js +170 -6
- package/dist/graph-tools.js +34 -0
- package/dist/lib/teams-url-parser.js +24 -0
- package/logs/mcp-server.log +10 -10
- package/package.json +1 -1
- package/src/endpoints.json +51 -2
package/README.md
CHANGED
|
@@ -96,9 +96,10 @@ MS365_MCP_OUTPUT_FORMAT=toon npx @softeria/ms-365-mcp-server
|
|
|
96
96
|
|
|
97
97
|
### Personal Account Tools (Available by default)
|
|
98
98
|
|
|
99
|
-
**Email (Outlook)**
|
|
100
|
-
<sub>list-mail-messages, list-mail-folders, list-mail-folder-messages, get-mail-message, send-mail,
|
|
101
|
-
delete-mail-message, create-draft-email, move-mail-message
|
|
99
|
+
**Email (Outlook)**
|
|
100
|
+
<sub>list-mail-messages, list-mail-folders, list-mail-child-folders, list-mail-folder-messages, get-mail-message, send-mail,
|
|
101
|
+
delete-mail-message, create-draft-email, move-mail-message, create-mail-folder, create-mail-child-folder,
|
|
102
|
+
update-mail-folder, delete-mail-folder</sub>
|
|
102
103
|
|
|
103
104
|
**Calendar**
|
|
104
105
|
<sub>list-calendars, list-calendar-events, get-calendar-event, get-calendar-view, create-calendar-event,
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a Vulnerability
|
|
4
|
+
|
|
5
|
+
If you discover a security vulnerability in this project, please report it
|
|
6
|
+
responsibly through **GitHub Private Vulnerability Reporting (PVR)**.
|
|
7
|
+
|
|
8
|
+
**To report**: Go to the [Security Advisories page](../../security/advisories/new)
|
|
9
|
+
and submit a new advisory.
|
|
10
|
+
|
|
11
|
+
Please **do not** open public issues for security vulnerabilities.
|
package/dist/endpoints.json
CHANGED
|
@@ -18,6 +18,34 @@
|
|
|
18
18
|
"toolName": "list-mail-child-folders",
|
|
19
19
|
"scopes": ["Mail.Read"]
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
"pathPattern": "/me/mailFolders",
|
|
23
|
+
"method": "post",
|
|
24
|
+
"toolName": "create-mail-folder",
|
|
25
|
+
"scopes": ["Mail.ReadWrite"],
|
|
26
|
+
"llmTip": "Creates a top-level mail folder. Use create-mail-child-folder to create a subfolder inside an existing folder. Use list-mail-folders to find existing folder IDs."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}/childFolders",
|
|
30
|
+
"method": "post",
|
|
31
|
+
"toolName": "create-mail-child-folder",
|
|
32
|
+
"scopes": ["Mail.ReadWrite"],
|
|
33
|
+
"llmTip": "Creates a subfolder inside an existing mail folder. Use list-mail-folders or list-mail-child-folders to find the parent folder ID."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}",
|
|
37
|
+
"method": "patch",
|
|
38
|
+
"toolName": "update-mail-folder",
|
|
39
|
+
"scopes": ["Mail.ReadWrite"],
|
|
40
|
+
"llmTip": "Renames a mail folder by updating its displayName. Use list-mail-folders to find the folder ID."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}",
|
|
44
|
+
"method": "delete",
|
|
45
|
+
"toolName": "delete-mail-folder",
|
|
46
|
+
"scopes": ["Mail.ReadWrite"],
|
|
47
|
+
"llmTip": "Deletes a mail folder and all its contents. This action is irreversible. Use list-mail-folders to find the folder ID."
|
|
48
|
+
},
|
|
21
49
|
{
|
|
22
50
|
"pathPattern": "/me/mailFolders/{mailFolder-id}/messages",
|
|
23
51
|
"method": "get",
|
|
@@ -245,7 +273,7 @@
|
|
|
245
273
|
"scopes": ["Calendars.Read"],
|
|
246
274
|
"supportsTimezone": true,
|
|
247
275
|
"supportsExpandExtendedProperties": true,
|
|
248
|
-
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar."
|
|
276
|
+
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar. To find Teams meetings, use $filter=isOnlineMeeting eq true. To search by subject, use $filter=contains(subject,'keyword'). Teams meetings include a joinWebUrl property needed for transcript access via list-online-meetings."
|
|
249
277
|
},
|
|
250
278
|
{
|
|
251
279
|
"pathPattern": "/me/calendars/{calendar-id}/calendarView",
|
|
@@ -254,7 +282,7 @@
|
|
|
254
282
|
"scopes": ["Calendars.Read"],
|
|
255
283
|
"supportsTimezone": true,
|
|
256
284
|
"supportsExpandExtendedProperties": true,
|
|
257
|
-
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events."
|
|
285
|
+
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events. To find Teams meetings, use $filter=isOnlineMeeting eq true. Teams meetings include a joinWebUrl property needed for transcript access via list-online-meetings."
|
|
258
286
|
},
|
|
259
287
|
{
|
|
260
288
|
"pathPattern": "/me/calendars/{calendar-id}/events/{event-id}/instances",
|
|
@@ -466,6 +494,12 @@
|
|
|
466
494
|
"toolName": "update-planner-task",
|
|
467
495
|
"scopes": ["Tasks.ReadWrite"]
|
|
468
496
|
},
|
|
497
|
+
{
|
|
498
|
+
"pathPattern": "/planner/tasks/{plannerTask-id}/details",
|
|
499
|
+
"method": "get",
|
|
500
|
+
"toolName": "get-planner-task-details",
|
|
501
|
+
"scopes": ["Tasks.Read"]
|
|
502
|
+
},
|
|
469
503
|
{
|
|
470
504
|
"pathPattern": "/planner/tasks/{plannerTask-id}/details",
|
|
471
505
|
"method": "patch",
|
|
@@ -714,6 +748,21 @@
|
|
|
714
748
|
"acceptType": "text/vtt",
|
|
715
749
|
"llmTip": "Returns the transcript content in WebVTT format with speaker identification and timestamps."
|
|
716
750
|
},
|
|
751
|
+
{
|
|
752
|
+
"pathPattern": "/me/onlineMeetings/{onlineMeeting-id}/recordings",
|
|
753
|
+
"method": "get",
|
|
754
|
+
"toolName": "list-meeting-recordings",
|
|
755
|
+
"workScopes": ["OnlineMeetingRecording.Read.All"],
|
|
756
|
+
"llmTip": "Lists recordings for a meeting. Each recording has createdDateTime, endDateTime, meetingOrganizer, contentCorrelationId (links to corresponding transcript). For recurring meetings, there is one recording per occurrence."
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
"pathPattern": "/me/onlineMeetings/{onlineMeeting-id}/recordings/{callRecording-id}/content",
|
|
760
|
+
"method": "get",
|
|
761
|
+
"toolName": "get-meeting-recording-content",
|
|
762
|
+
"workScopes": ["OnlineMeetingRecording.Read.All"],
|
|
763
|
+
"returnDownloadUrl": true,
|
|
764
|
+
"llmTip": "Returns a temporary download URL for the meeting recording in MP4 format. Use the download URL to access the video file. The recording may be large — do not attempt to stream it inline."
|
|
765
|
+
},
|
|
717
766
|
{
|
|
718
767
|
"pathPattern": "/groups/{group-id}/conversations",
|
|
719
768
|
"method": "get",
|
package/dist/generated/client.js
CHANGED
|
@@ -2873,6 +2873,32 @@ const microsoft_graph_onlineMeetingCollectionResponse = z.object({
|
|
|
2873
2873
|
"@odata.nextLink": z.string().nullable(),
|
|
2874
2874
|
value: z.array(microsoft_graph_onlineMeeting)
|
|
2875
2875
|
}).partial().passthrough();
|
|
2876
|
+
const microsoft_graph_callRecording = z.object({
|
|
2877
|
+
id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
|
|
2878
|
+
callId: z.string().describe("The unique identifier for the call that is related to this recording. Read-only.").nullish(),
|
|
2879
|
+
content: z.string().describe("The content of the recording. Read-only.").nullish(),
|
|
2880
|
+
contentCorrelationId: z.string().describe(
|
|
2881
|
+
"The unique identifier that links the transcript with its corresponding recording. Read-only."
|
|
2882
|
+
).nullish(),
|
|
2883
|
+
createdDateTime: z.string().regex(
|
|
2884
|
+
/^[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])$/
|
|
2885
|
+
).datetime({ offset: true }).describe(
|
|
2886
|
+
"Date and time at which the recording was created. The timestamp type represents date and time information using ISO 8601 format and is always in UTC. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z. Read-only."
|
|
2887
|
+
).nullish(),
|
|
2888
|
+
endDateTime: z.string().regex(
|
|
2889
|
+
/^[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])$/
|
|
2890
|
+
).datetime({ offset: true }).describe(
|
|
2891
|
+
"Date and time at which the recording ends. The timestamp type represents date and time information using ISO 8601 format and is always in UTC. For example, midnight UTC on Jan 1, 2014 is 2014-01-01T00:00:00Z. Read-only."
|
|
2892
|
+
).nullish(),
|
|
2893
|
+
meetingId: z.string().describe("The unique identifier of the onlineMeeting related to this recording. Read-only.").nullish(),
|
|
2894
|
+
meetingOrganizer: microsoft_graph_identitySet.optional(),
|
|
2895
|
+
recordingContentUrl: z.string().describe("The URL that can be used to access the content of the recording. Read-only.").nullish()
|
|
2896
|
+
}).passthrough();
|
|
2897
|
+
const microsoft_graph_callRecordingCollectionResponse = z.object({
|
|
2898
|
+
"@odata.count": z.number().int().nullable(),
|
|
2899
|
+
"@odata.nextLink": z.string().nullable(),
|
|
2900
|
+
value: z.array(microsoft_graph_callRecording)
|
|
2901
|
+
}).partial().passthrough();
|
|
2876
2902
|
const microsoft_graph_callTranscript = z.object({
|
|
2877
2903
|
id: z.string().describe("The unique identifier for an entity. Read-only.").optional(),
|
|
2878
2904
|
callId: z.string().describe("The unique identifier for the call that is related to this transcript. Read-only.").nullish(),
|
|
@@ -3752,6 +3778,8 @@ const schemas = {
|
|
|
3752
3778
|
microsoft_graph_joinMeetingIdSettings,
|
|
3753
3779
|
microsoft_graph_onlineMeeting,
|
|
3754
3780
|
microsoft_graph_onlineMeetingCollectionResponse,
|
|
3781
|
+
microsoft_graph_callRecording,
|
|
3782
|
+
microsoft_graph_callRecordingCollectionResponse,
|
|
3755
3783
|
microsoft_graph_callTranscript,
|
|
3756
3784
|
microsoft_graph_callTranscriptCollectionResponse,
|
|
3757
3785
|
microsoft_graph_plannerChecklistItems,
|
|
@@ -5482,6 +5510,53 @@ Based on this value, you can better adjust the parameters and call findMeetingTi
|
|
|
5482
5510
|
],
|
|
5483
5511
|
response: z.void()
|
|
5484
5512
|
},
|
|
5513
|
+
{
|
|
5514
|
+
method: "post",
|
|
5515
|
+
path: "/me/mailFolders",
|
|
5516
|
+
alias: "create-mail-folder",
|
|
5517
|
+
description: `Use this API to create a new mail folder in the root folder of the user's mailbox. If you intend a new folder to be hidden, you must set the isHidden property to true on creation.`,
|
|
5518
|
+
requestFormat: "json",
|
|
5519
|
+
parameters: [
|
|
5520
|
+
{
|
|
5521
|
+
name: "body",
|
|
5522
|
+
description: `New navigation property`,
|
|
5523
|
+
type: "Body",
|
|
5524
|
+
schema: microsoft_graph_mailFolder
|
|
5525
|
+
}
|
|
5526
|
+
],
|
|
5527
|
+
response: z.void()
|
|
5528
|
+
},
|
|
5529
|
+
{
|
|
5530
|
+
method: "patch",
|
|
5531
|
+
path: "/me/mailFolders/:mailFolderId",
|
|
5532
|
+
alias: "update-mail-folder",
|
|
5533
|
+
description: `Update the properties of mailfolder object.`,
|
|
5534
|
+
requestFormat: "json",
|
|
5535
|
+
parameters: [
|
|
5536
|
+
{
|
|
5537
|
+
name: "body",
|
|
5538
|
+
description: `New navigation property values`,
|
|
5539
|
+
type: "Body",
|
|
5540
|
+
schema: microsoft_graph_mailFolder
|
|
5541
|
+
}
|
|
5542
|
+
],
|
|
5543
|
+
response: z.void()
|
|
5544
|
+
},
|
|
5545
|
+
{
|
|
5546
|
+
method: "delete",
|
|
5547
|
+
path: "/me/mailFolders/:mailFolderId",
|
|
5548
|
+
alias: "delete-mail-folder",
|
|
5549
|
+
description: `Delete the specified mailFolder. The folder can be a mailSearchFolder. You can specify a mail folder by its folder ID, or by its well-known folder name, if one exists.`,
|
|
5550
|
+
requestFormat: "json",
|
|
5551
|
+
parameters: [
|
|
5552
|
+
{
|
|
5553
|
+
name: "If-Match",
|
|
5554
|
+
type: "Header",
|
|
5555
|
+
schema: z.string().describe("ETag").optional()
|
|
5556
|
+
}
|
|
5557
|
+
],
|
|
5558
|
+
response: z.void()
|
|
5559
|
+
},
|
|
5485
5560
|
{
|
|
5486
5561
|
method: "get",
|
|
5487
5562
|
path: "/me/mailFolders/:mailFolderId/childFolders",
|
|
@@ -5538,6 +5613,22 @@ folder collection and navigate to another folder. By default, this operation doe
|
|
|
5538
5613
|
],
|
|
5539
5614
|
response: z.void()
|
|
5540
5615
|
},
|
|
5616
|
+
{
|
|
5617
|
+
method: "post",
|
|
5618
|
+
path: "/me/mailFolders/:mailFolderId/childFolders",
|
|
5619
|
+
alias: "create-mail-child-folder",
|
|
5620
|
+
description: `Use this API to create a new child mailFolder. If you intend a new folder to be hidden, you must set the isHidden property to true on creation.`,
|
|
5621
|
+
requestFormat: "json",
|
|
5622
|
+
parameters: [
|
|
5623
|
+
{
|
|
5624
|
+
name: "body",
|
|
5625
|
+
description: `New navigation property`,
|
|
5626
|
+
type: "Body",
|
|
5627
|
+
schema: microsoft_graph_mailFolder
|
|
5628
|
+
}
|
|
5629
|
+
],
|
|
5630
|
+
response: z.void()
|
|
5631
|
+
},
|
|
5541
5632
|
{
|
|
5542
5633
|
method: "get",
|
|
5543
5634
|
path: "/me/mailFolders/:mailFolderId/messages",
|
|
@@ -5908,12 +5999,7 @@ resource.`,
|
|
|
5908
5999
|
method: "post",
|
|
5909
6000
|
path: "/me/messages/:messageId/createForward",
|
|
5910
6001
|
alias: "create-forward-draft",
|
|
5911
|
-
description: `
|
|
5912
|
-
- Specify either a comment or the body property of the message parameter. Specifying both will return an HTTP 400 Bad Request error.
|
|
5913
|
-
- Specify either the toRecipients parameter or the toRecipients property of the message parameter. Specifying both or specifying neither will return an HTTP 400 Bad Request error.
|
|
5914
|
-
- Update the draft later to add content to the body or change other message properties. When using MIME format:
|
|
5915
|
-
- Provide the applicable Internet message headers and the MIME content, all encoded in base64 format in the request body.
|
|
5916
|
-
- Add any attachments and S/MIME properties to the MIME content. Send the draft message in a subsequent operation. Alternatively, forward a message in a single operation.`,
|
|
6002
|
+
description: `Invoke action createForward`,
|
|
5917
6003
|
requestFormat: "json",
|
|
5918
6004
|
parameters: [
|
|
5919
6005
|
{
|
|
@@ -6291,6 +6377,64 @@ resource.`,
|
|
|
6291
6377
|
],
|
|
6292
6378
|
response: z.void()
|
|
6293
6379
|
},
|
|
6380
|
+
{
|
|
6381
|
+
method: "get",
|
|
6382
|
+
path: "/me/onlineMeetings/:onlineMeetingId/recordings",
|
|
6383
|
+
alias: "list-meeting-recordings",
|
|
6384
|
+
description: `Get a callRecording object associated with a scheduled online meeting and an ad hoc call. This API supports the retrieval of call recordings from all meeting types except live events. For a recording, this API returns the metadata of the single recording associated with the online meeting or an ad hoc call. For the content of a recording, this API returns the stream of bytes associated with the recording.`,
|
|
6385
|
+
requestFormat: "json",
|
|
6386
|
+
parameters: [
|
|
6387
|
+
{
|
|
6388
|
+
name: "$top",
|
|
6389
|
+
type: "Query",
|
|
6390
|
+
schema: z.number().int().gte(0).describe("Show only the first n items").optional()
|
|
6391
|
+
},
|
|
6392
|
+
{
|
|
6393
|
+
name: "$skip",
|
|
6394
|
+
type: "Query",
|
|
6395
|
+
schema: z.number().int().gte(0).describe("Skip the first n items").optional()
|
|
6396
|
+
},
|
|
6397
|
+
{
|
|
6398
|
+
name: "$search",
|
|
6399
|
+
type: "Query",
|
|
6400
|
+
schema: z.string().describe("Search items by search phrases").optional()
|
|
6401
|
+
},
|
|
6402
|
+
{
|
|
6403
|
+
name: "$filter",
|
|
6404
|
+
type: "Query",
|
|
6405
|
+
schema: z.string().describe("Filter items by property values").optional()
|
|
6406
|
+
},
|
|
6407
|
+
{
|
|
6408
|
+
name: "$count",
|
|
6409
|
+
type: "Query",
|
|
6410
|
+
schema: z.boolean().describe("Include count of items").optional()
|
|
6411
|
+
},
|
|
6412
|
+
{
|
|
6413
|
+
name: "$orderby",
|
|
6414
|
+
type: "Query",
|
|
6415
|
+
schema: z.array(z.string()).describe("Order items by property values").optional()
|
|
6416
|
+
},
|
|
6417
|
+
{
|
|
6418
|
+
name: "$select",
|
|
6419
|
+
type: "Query",
|
|
6420
|
+
schema: z.array(z.string()).describe("Select properties to be returned").optional()
|
|
6421
|
+
},
|
|
6422
|
+
{
|
|
6423
|
+
name: "$expand",
|
|
6424
|
+
type: "Query",
|
|
6425
|
+
schema: z.array(z.string()).describe("Expand related entities").optional()
|
|
6426
|
+
}
|
|
6427
|
+
],
|
|
6428
|
+
response: z.void()
|
|
6429
|
+
},
|
|
6430
|
+
{
|
|
6431
|
+
method: "get",
|
|
6432
|
+
path: "/me/onlineMeetings/:onlineMeetingId/recordings/:callRecordingId/content",
|
|
6433
|
+
alias: "get-meeting-recording-content",
|
|
6434
|
+
description: `The content of the recording. Read-only.`,
|
|
6435
|
+
requestFormat: "json",
|
|
6436
|
+
response: z.void()
|
|
6437
|
+
},
|
|
6294
6438
|
{
|
|
6295
6439
|
method: "get",
|
|
6296
6440
|
path: "/me/onlineMeetings/:onlineMeetingId/transcripts",
|
|
@@ -6825,6 +6969,26 @@ resource.`,
|
|
|
6825
6969
|
],
|
|
6826
6970
|
response: z.void()
|
|
6827
6971
|
},
|
|
6972
|
+
{
|
|
6973
|
+
method: "get",
|
|
6974
|
+
path: "/planner/tasks/:plannerTaskId/details",
|
|
6975
|
+
alias: "get-planner-task-details",
|
|
6976
|
+
description: `Retrieve the properties and relationships of a plannerTaskDetails object.`,
|
|
6977
|
+
requestFormat: "json",
|
|
6978
|
+
parameters: [
|
|
6979
|
+
{
|
|
6980
|
+
name: "$select",
|
|
6981
|
+
type: "Query",
|
|
6982
|
+
schema: z.array(z.string()).describe("Select properties to be returned").optional()
|
|
6983
|
+
},
|
|
6984
|
+
{
|
|
6985
|
+
name: "$expand",
|
|
6986
|
+
type: "Query",
|
|
6987
|
+
schema: z.array(z.string()).describe("Expand related entities").optional()
|
|
6988
|
+
}
|
|
6989
|
+
],
|
|
6990
|
+
response: z.void()
|
|
6991
|
+
},
|
|
6828
6992
|
{
|
|
6829
6993
|
method: "patch",
|
|
6830
6994
|
path: "/planner/tasks/:plannerTaskId/details",
|
package/dist/graph-tools.js
CHANGED
|
@@ -6,6 +6,7 @@ import path from "path";
|
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { TOOL_CATEGORIES } from "./tool-categories.js";
|
|
8
8
|
import { getRequestTokens } from "./request-context.js";
|
|
9
|
+
import { parseTeamsUrl } from "./lib/teams-url-parser.js";
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
11
12
|
const endpointsData = JSON.parse(
|
|
@@ -349,6 +350,39 @@ function registerGraphTools(server, graphClient, readOnly = false, enabledToolsP
|
|
|
349
350
|
if (multiAccount) {
|
|
350
351
|
logger.info('Multi-account mode: "account" parameter injected into all tool schemas');
|
|
351
352
|
}
|
|
353
|
+
if (!enabledToolsRegex || enabledToolsRegex.test("parse-teams-url")) {
|
|
354
|
+
try {
|
|
355
|
+
server.tool(
|
|
356
|
+
"parse-teams-url",
|
|
357
|
+
"Converts any Teams meeting URL format (short /meet/, full /meetup-join/, or recap ?threadId=) into a standard joinWebUrl. Use this before list-online-meetings when the user provides a recap or short URL.",
|
|
358
|
+
{
|
|
359
|
+
url: z.string().describe("Teams meeting URL in any format")
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
title: "parse-teams-url",
|
|
363
|
+
readOnlyHint: true,
|
|
364
|
+
openWorldHint: false
|
|
365
|
+
},
|
|
366
|
+
async ({ url }) => {
|
|
367
|
+
try {
|
|
368
|
+
const joinWebUrl = parseTeamsUrl(url);
|
|
369
|
+
return { content: [{ type: "text", text: joinWebUrl }] };
|
|
370
|
+
} catch (error) {
|
|
371
|
+
return {
|
|
372
|
+
content: [
|
|
373
|
+
{ type: "text", text: JSON.stringify({ error: error.message }) }
|
|
374
|
+
],
|
|
375
|
+
isError: true
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
registeredCount++;
|
|
381
|
+
} catch (error) {
|
|
382
|
+
logger.error(`Failed to register tool parse-teams-url: ${error.message}`);
|
|
383
|
+
failedCount++;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
352
386
|
logger.info(
|
|
353
387
|
`Tool registration complete: ${registeredCount} registered, ${skippedCount} skipped, ${failedCount} failed`
|
|
354
388
|
);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function parseTeamsUrl(url) {
|
|
2
|
+
if (url.includes("/meet/") || url.includes("/meetup-join/")) {
|
|
3
|
+
return url;
|
|
4
|
+
}
|
|
5
|
+
if (url.toLowerCase().includes("meetingrecap")) {
|
|
6
|
+
const params = Object.fromEntries(
|
|
7
|
+
[...url.matchAll(/([a-zA-Z]+)=([^&#]+)/g)].map((m) => [m[1], m[2]])
|
|
8
|
+
);
|
|
9
|
+
const threadId = decodeURIComponent(params.threadId || "");
|
|
10
|
+
const tenantId = params.tenantId || "";
|
|
11
|
+
const organizerId = params.organizerId || "";
|
|
12
|
+
if (!threadId || !tenantId || !organizerId) {
|
|
13
|
+
throw new Error("Invalid recap URL: missing threadId, tenantId, or organizerId parameter");
|
|
14
|
+
}
|
|
15
|
+
const threadEnc = encodeURIComponent(threadId).replace(/%3A/gi, "%3a").replace(/%40/gi, "%40");
|
|
16
|
+
const ctx = JSON.stringify({ Tid: tenantId, Oid: organizerId });
|
|
17
|
+
const ctxEnc = encodeURIComponent(ctx);
|
|
18
|
+
return `https://teams.microsoft.com/l/meetup-join/${threadEnc}/0?context=${ctxEnc}`;
|
|
19
|
+
}
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
parseTeamsUrl
|
|
24
|
+
};
|
package/logs/mcp-server.log
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
2026-03-
|
|
2
|
-
2026-03-
|
|
3
|
-
2026-03-
|
|
4
|
-
2026-03-
|
|
5
|
-
2026-03-
|
|
6
|
-
2026-03-
|
|
7
|
-
2026-03-
|
|
8
|
-
2026-03-
|
|
9
|
-
2026-03-
|
|
10
|
-
2026-03-
|
|
1
|
+
2026-03-23 20:16:55 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
2
|
+
2026-03-23 20:16:55 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
3
|
+
2026-03-23 20:16:55 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me
|
|
4
|
+
2026-03-23 20:16:55 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/messages
|
|
5
|
+
2026-03-23 20:16:55 INFO: [GRAPH CLIENT] Final URL being sent to Microsoft: https://graph.microsoft.com/v1.0/me/calendar
|
|
6
|
+
2026-03-23 20:16:56 INFO: Using environment variables for secrets
|
|
7
|
+
2026-03-23 20:16:56 INFO: Using environment variables for secrets
|
|
8
|
+
2026-03-23 20:16:56 INFO: Using environment variables for secrets
|
|
9
|
+
2026-03-23 20:16:56 INFO: Using environment variables for secrets
|
|
10
|
+
2026-03-23 20:16:56 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.46.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
|
@@ -18,6 +18,34 @@
|
|
|
18
18
|
"toolName": "list-mail-child-folders",
|
|
19
19
|
"scopes": ["Mail.Read"]
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
"pathPattern": "/me/mailFolders",
|
|
23
|
+
"method": "post",
|
|
24
|
+
"toolName": "create-mail-folder",
|
|
25
|
+
"scopes": ["Mail.ReadWrite"],
|
|
26
|
+
"llmTip": "Creates a top-level mail folder. Use create-mail-child-folder to create a subfolder inside an existing folder. Use list-mail-folders to find existing folder IDs."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}/childFolders",
|
|
30
|
+
"method": "post",
|
|
31
|
+
"toolName": "create-mail-child-folder",
|
|
32
|
+
"scopes": ["Mail.ReadWrite"],
|
|
33
|
+
"llmTip": "Creates a subfolder inside an existing mail folder. Use list-mail-folders or list-mail-child-folders to find the parent folder ID."
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}",
|
|
37
|
+
"method": "patch",
|
|
38
|
+
"toolName": "update-mail-folder",
|
|
39
|
+
"scopes": ["Mail.ReadWrite"],
|
|
40
|
+
"llmTip": "Renames a mail folder by updating its displayName. Use list-mail-folders to find the folder ID."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"pathPattern": "/me/mailFolders/{mailFolder-id}",
|
|
44
|
+
"method": "delete",
|
|
45
|
+
"toolName": "delete-mail-folder",
|
|
46
|
+
"scopes": ["Mail.ReadWrite"],
|
|
47
|
+
"llmTip": "Deletes a mail folder and all its contents. This action is irreversible. Use list-mail-folders to find the folder ID."
|
|
48
|
+
},
|
|
21
49
|
{
|
|
22
50
|
"pathPattern": "/me/mailFolders/{mailFolder-id}/messages",
|
|
23
51
|
"method": "get",
|
|
@@ -245,7 +273,7 @@
|
|
|
245
273
|
"scopes": ["Calendars.Read"],
|
|
246
274
|
"supportsTimezone": true,
|
|
247
275
|
"supportsExpandExtendedProperties": true,
|
|
248
|
-
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar."
|
|
276
|
+
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for the default calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Use get-specific-calendar-view if you need a non-default calendar. To find Teams meetings, use $filter=isOnlineMeeting eq true. To search by subject, use $filter=contains(subject,'keyword'). Teams meetings include a joinWebUrl property needed for transcript access via list-online-meetings."
|
|
249
277
|
},
|
|
250
278
|
{
|
|
251
279
|
"pathPattern": "/me/calendars/{calendar-id}/calendarView",
|
|
@@ -254,7 +282,7 @@
|
|
|
254
282
|
"scopes": ["Calendars.Read"],
|
|
255
283
|
"supportsTimezone": true,
|
|
256
284
|
"supportsExpandExtendedProperties": true,
|
|
257
|
-
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events."
|
|
285
|
+
"llmTip": "Returns expanded recurring event instances (not just seriesMaster) within a date range for a specific calendar. Requires startDateTime and endDateTime query parameters in ISO 8601 format (e.g., 2024-01-01T00:00:00Z). Each instance includes seriesMasterId and type (occurrence/exception) fields for recurring event linkage. Use fetchAllPages=true to retrieve all results when there are many events. To find Teams meetings, use $filter=isOnlineMeeting eq true. Teams meetings include a joinWebUrl property needed for transcript access via list-online-meetings."
|
|
258
286
|
},
|
|
259
287
|
{
|
|
260
288
|
"pathPattern": "/me/calendars/{calendar-id}/events/{event-id}/instances",
|
|
@@ -466,6 +494,12 @@
|
|
|
466
494
|
"toolName": "update-planner-task",
|
|
467
495
|
"scopes": ["Tasks.ReadWrite"]
|
|
468
496
|
},
|
|
497
|
+
{
|
|
498
|
+
"pathPattern": "/planner/tasks/{plannerTask-id}/details",
|
|
499
|
+
"method": "get",
|
|
500
|
+
"toolName": "get-planner-task-details",
|
|
501
|
+
"scopes": ["Tasks.Read"]
|
|
502
|
+
},
|
|
469
503
|
{
|
|
470
504
|
"pathPattern": "/planner/tasks/{plannerTask-id}/details",
|
|
471
505
|
"method": "patch",
|
|
@@ -714,6 +748,21 @@
|
|
|
714
748
|
"acceptType": "text/vtt",
|
|
715
749
|
"llmTip": "Returns the transcript content in WebVTT format with speaker identification and timestamps."
|
|
716
750
|
},
|
|
751
|
+
{
|
|
752
|
+
"pathPattern": "/me/onlineMeetings/{onlineMeeting-id}/recordings",
|
|
753
|
+
"method": "get",
|
|
754
|
+
"toolName": "list-meeting-recordings",
|
|
755
|
+
"workScopes": ["OnlineMeetingRecording.Read.All"],
|
|
756
|
+
"llmTip": "Lists recordings for a meeting. Each recording has createdDateTime, endDateTime, meetingOrganizer, contentCorrelationId (links to corresponding transcript). For recurring meetings, there is one recording per occurrence."
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
"pathPattern": "/me/onlineMeetings/{onlineMeeting-id}/recordings/{callRecording-id}/content",
|
|
760
|
+
"method": "get",
|
|
761
|
+
"toolName": "get-meeting-recording-content",
|
|
762
|
+
"workScopes": ["OnlineMeetingRecording.Read.All"],
|
|
763
|
+
"returnDownloadUrl": true,
|
|
764
|
+
"llmTip": "Returns a temporary download URL for the meeting recording in MP4 format. Use the download URL to access the video file. The recording may be large — do not attempt to stream it inline."
|
|
765
|
+
},
|
|
717
766
|
{
|
|
718
767
|
"pathPattern": "/groups/{group-id}/conversations",
|
|
719
768
|
"method": "get",
|