@softeria/ms-365-mcp-server 0.6.0 → 0.6.2

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.
@@ -213,7 +213,7 @@
213
213
  ]
214
214
  },
215
215
  {
216
- "pathPattern": "/me/onenote/notebooks/{notebook-id}/sections/{onenoteSection-id}/pages",
216
+ "pathPattern": "/me/onenote/sections/{onenoteSection-id}/pages",
217
217
  "method": "get",
218
218
  "toolName": "list-onenote-section-pages",
219
219
  "scopes": [
@@ -13435,13 +13435,8 @@ get the instances of an event. Currently, this operation returns event bodies in
13435
13435
  method: 'post',
13436
13436
  path: '/me/events',
13437
13437
  alias: 'create-calendar-event',
13438
- description: `Create an event in the user's default calendar or specified calendar. By default, the allowNewTimeProposals property is set to true when an event is created, which means invitees can propose a different date/time for the event. See Propose new meeting times for more information on how to propose a time, and how to receive and accept a new time proposal. You can specify the time zone for each of the start and end times of the event as part of their values, because the
13439
- start and end properties are of dateTimeTimeZone type. First find the supported time zones to make sure you set only time zones that have been configured for the user's mailbox server. When an event is sent, the server sends invitations to all the attendees. Setting the location in an event An Exchange administrator can set up a mailbox and an email address for a resource such as a meeting room, or equipment
13440
- like a projector. Users can then invite the resource as an attendee to a meeting. On behalf of the resource, the server accepts or rejects
13441
- the meeting request based on the free/busy schedule of the resource.
13442
- If the server accepts a meeting for the resource, it creates an event for the meeting in the resource's calendar. If the meeting is rescheduled,
13443
- the server automatically updates the event in the resource's calendar. Another advantage of setting up a mailbox for a resource is to control scheduling of the resource, for example, only executives
13444
- or their delegates can book a private meeting room. If you're organizing an event that involves a meeting location: Additionally, if the meeting location has been set up as a resource, or if the event involves some equipment that has been set up as a resource:`,
13438
+ description: `Create one or more multi-value extended properties in a new or existing instance of a resource. The following user resources are supported: The following group resources are supported: See Extended properties overview for more information about when to use
13439
+ open extensions or extended properties, and how to specify extended properties.`,
13445
13440
  requestFormat: 'json',
13446
13441
  parameters: [
13447
13442
  {
@@ -13710,7 +13705,7 @@ or their delegates can book a private meeting room. If you're organizing an
13710
13705
  method: 'get',
13711
13706
  path: '/me/messages',
13712
13707
  alias: 'list-mail-messages',
13713
- description: `Get an open extension (openTypeExtension object) identified by name or fully qualified name. The table in the Permissions section lists the resources that support open extensions. The following table lists the three scenarios where you can get an open extension from a supported resource instance.`,
13708
+ description: `Get the messages in the signed-in user's mailbox (including the Deleted Items and Clutter folders). Depending on the page size and mailbox data, getting messages from a mailbox can incur multiple requests. The default page size is 10 messages. Use $top to customize the page size, within the range of 1 and 1000. To improve the operation response time, use $select to specify the exact properties you need; see example 1 below. Fine-tune the values for $select and $top, especially when you must use a larger page size, as returning a page with hundreds of messages each with a full response payload may trigger the gateway timeout (HTTP 504). To get the next page of messages, simply apply the entire URL returned in @odata.nextLink to the next get-messages request. This URL includes any query parameters you may have specified in the initial request. Do not try to extract the $skip value from the @odata.nextLink URL to manipulate responses. This API uses the $skip value to keep count of all the items it has gone through in the user's mailbox to return a page of message-type items. It's therefore possible that even in the initial response, the $skip value is larger than the page size. For more information, see Paging Microsoft Graph data in your app. Currently, this operation returns message bodies in only HTML format. There are two scenarios where an app can get messages in another user's mail folder:`,
13714
13709
  requestFormat: 'json',
13715
13710
  parameters: [
13716
13711
  {
@@ -13782,13 +13777,7 @@ or their delegates can book a private meeting room. If you're organizing an
13782
13777
  method: 'get',
13783
13778
  path: '/me/messages/:messageId',
13784
13779
  alias: 'get-mail-message',
13785
- description: `You can get a single resource instance expanded with a specific extended property, or a collection of resource instances
13786
- that include extended properties matching a filter. Using the query parameter $expand allows you to get the specified resource instance expanded with a specific extended
13787
- property. Use a $filter and eq operator on the id property to specify the extended property. This is currently the only way to get the singleValueLegacyExtendedProperty object that represents an extended property. To get resource instances that have certain extended properties, use the $filter query parameter and apply an eq operator
13788
- on the id property. In addition, for numeric extended properties, apply one of the following operators on the value property:
13789
- eq, ne,ge, gt, le, or lt. For string-typed extended properties, apply a contains, startswith, eq, or ne operator on value. The filter is applied to all instances of the resource in the signed-in user's mailbox. Filtering the string name (Name) in the id of an extended property is case-sensitive. Filtering the value property of an extended
13790
- property is case-insensitive. The following user resources are supported: As well as the following group resources: See Extended properties overview for more information about when to use
13791
- open extensions or extended properties, and how to specify extended properties.`,
13780
+ description: `Get an open extension (openTypeExtension object) identified by name or fully qualified name. The table in the Permissions section lists the resources that support open extensions. The following table lists the three scenarios where you can get an open extension from a supported resource instance.`,
13792
13781
  requestFormat: 'json',
13793
13782
  parameters: [
13794
13783
  {
@@ -13825,7 +13814,7 @@ open extensions or extended properties, and how to specify extended properties.`
13825
13814
  method: 'delete',
13826
13815
  path: '/me/messages/:messageId',
13827
13816
  alias: 'delete-mail-message',
13828
- description: `Delete eventMessage.`,
13817
+ description: `Delete a message in the specified user's mailbox, or delete a relationship of the message.`,
13829
13818
  requestFormat: 'json',
13830
13819
  parameters: [
13831
13820
  {
@@ -13982,11 +13971,69 @@ open extensions or extended properties, and how to specify extended properties.`
13982
13971
  },
13983
13972
  ],
13984
13973
  },
13974
+ {
13975
+ method: 'post',
13976
+ path: '/me/onenote/pages',
13977
+ alias: 'create-onenote-page',
13978
+ description: `Create a new OneNote page in the default section of the default notebook. To create a page in a different section in the default notebook, you can use the sectionName query parameter. Example: ../onenote/pages?sectionName=My%20section The POST /onenote/pages operation is used only to create pages in the current user's default notebook. If you're targeting other notebooks, you can create pages in a specified section. `,
13979
+ requestFormat: 'json',
13980
+ parameters: [
13981
+ {
13982
+ name: 'body',
13983
+ description: `New navigation property`,
13984
+ type: 'Body',
13985
+ schema: microsoft_graph_onenotePage,
13986
+ },
13987
+ ],
13988
+ response: z.void(),
13989
+ errors: [
13990
+ {
13991
+ status: NaN,
13992
+ description: `Created navigation property.`,
13993
+ schema: microsoft_graph_onenotePage,
13994
+ },
13995
+ {
13996
+ status: NaN,
13997
+ description: `error`,
13998
+ schema: microsoft_graph_ODataErrors_ODataError,
13999
+ },
14000
+ {
14001
+ status: NaN,
14002
+ description: `error`,
14003
+ schema: microsoft_graph_ODataErrors_ODataError,
14004
+ },
14005
+ ],
14006
+ },
14007
+ {
14008
+ method: 'get',
14009
+ path: '/me/onenote/pages/:onenotePageId/content',
14010
+ alias: 'get-onenote-page-content',
14011
+ description: `The page's HTML content.`,
14012
+ requestFormat: 'json',
14013
+ response: z.void(),
14014
+ errors: [
14015
+ {
14016
+ status: NaN,
14017
+ description: `Retrieved media content`,
14018
+ schema: z.void(),
14019
+ },
14020
+ {
14021
+ status: NaN,
14022
+ description: `error`,
14023
+ schema: microsoft_graph_ODataErrors_ODataError,
14024
+ },
14025
+ {
14026
+ status: NaN,
14027
+ description: `error`,
14028
+ schema: microsoft_graph_ODataErrors_ODataError,
14029
+ },
14030
+ ],
14031
+ },
13985
14032
  {
13986
14033
  method: 'get',
13987
- path: '/me/onenote/notebooks/:notebookId/sections/:onenoteSectionId/pages',
14034
+ path: '/me/onenote/sections/:onenoteSectionId/pages',
13988
14035
  alias: 'list-onenote-section-pages',
13989
- description: `The collection of pages in the section. Read-only. Nullable.`,
14036
+ description: `Retrieve a list of page objects from the specified section.`,
13990
14037
  requestFormat: 'json',
13991
14038
  parameters: [
13992
14039
  {
@@ -14049,64 +14096,6 @@ open extensions or extended properties, and how to specify extended properties.`
14049
14096
  },
14050
14097
  ],
14051
14098
  },
14052
- {
14053
- method: 'post',
14054
- path: '/me/onenote/pages',
14055
- alias: 'create-onenote-page',
14056
- description: `Create a new OneNote page in the default section of the default notebook. To create a page in a different section in the default notebook, you can use the sectionName query parameter. Example: ../onenote/pages?sectionName=My%20section The POST /onenote/pages operation is used only to create pages in the current user's default notebook. If you're targeting other notebooks, you can create pages in a specified section. `,
14057
- requestFormat: 'json',
14058
- parameters: [
14059
- {
14060
- name: 'body',
14061
- description: `New navigation property`,
14062
- type: 'Body',
14063
- schema: microsoft_graph_onenotePage,
14064
- },
14065
- ],
14066
- response: z.void(),
14067
- errors: [
14068
- {
14069
- status: NaN,
14070
- description: `Created navigation property.`,
14071
- schema: microsoft_graph_onenotePage,
14072
- },
14073
- {
14074
- status: NaN,
14075
- description: `error`,
14076
- schema: microsoft_graph_ODataErrors_ODataError,
14077
- },
14078
- {
14079
- status: NaN,
14080
- description: `error`,
14081
- schema: microsoft_graph_ODataErrors_ODataError,
14082
- },
14083
- ],
14084
- },
14085
- {
14086
- method: 'get',
14087
- path: '/me/onenote/pages/:onenotePageId/content',
14088
- alias: 'get-onenote-page-content',
14089
- description: `The page's HTML content.`,
14090
- requestFormat: 'json',
14091
- response: z.void(),
14092
- errors: [
14093
- {
14094
- status: NaN,
14095
- description: `Retrieved media content`,
14096
- schema: z.void(),
14097
- },
14098
- {
14099
- status: NaN,
14100
- description: `error`,
14101
- schema: microsoft_graph_ODataErrors_ODataError,
14102
- },
14103
- {
14104
- status: NaN,
14105
- description: `error`,
14106
- schema: microsoft_graph_ODataErrors_ODataError,
14107
- },
14108
- ],
14109
- },
14110
14099
  {
14111
14100
  method: 'get',
14112
14101
  path: '/me/planner/tasks',
@@ -71,10 +71,10 @@ class GraphClient {
71
71
  };
72
72
  }
73
73
  const headers = {
74
- ...options.headers,
75
74
  Authorization: `Bearer ${accessToken}`,
76
75
  'Content-Type': 'application/json',
77
76
  ...(sessionId && { 'workbook-session-id': sessionId }),
77
+ ...options.headers,
78
78
  };
79
79
  delete options.headers;
80
80
  logger.info(` ** Making request to ${url} with options: ${JSON.stringify(options)}`);
@@ -155,6 +155,27 @@ class GraphClient {
155
155
  ],
156
156
  };
157
157
  }
158
+ const contentType = response.headers.get('content-type');
159
+ if (contentType && !contentType.includes('application/json')) {
160
+ if (contentType.startsWith('text/')) {
161
+ const text = await response.text();
162
+ return {
163
+ content: [{ type: 'text', text }],
164
+ };
165
+ }
166
+ return {
167
+ content: [
168
+ {
169
+ type: 'text',
170
+ text: JSON.stringify({
171
+ message: 'Binary or non-JSON content received',
172
+ contentType: contentType,
173
+ contentLength: response.headers.get('content-length'),
174
+ }),
175
+ },
176
+ ],
177
+ };
178
+ }
158
179
  const result = await response.json();
159
180
  const removeODataProps = (obj) => {
160
181
  if (!obj || typeof obj !== 'object')
@@ -10,8 +10,12 @@ export function registerGraphTools(server, graphClient, readOnly = false) {
10
10
  const paramSchema = {};
11
11
  if (tool.parameters && tool.parameters.length > 0) {
12
12
  for (const param of tool.parameters) {
13
- // Use z.any() as a fallback schema if the parameter schema is not specified
14
- paramSchema[param.name] = param.schema || z.any();
13
+ if (param.type === 'Body' && param.schema) {
14
+ paramSchema[param.name] = z.union([z.string(), param.schema]);
15
+ }
16
+ else {
17
+ paramSchema[param.name] = param.schema || z.any();
18
+ }
15
19
  }
16
20
  }
17
21
  server.tool(tool.alias, tool.description ?? '', paramSchema, {
@@ -55,7 +59,17 @@ export function registerGraphTools(server, graphClient, readOnly = false) {
55
59
  queryParams[fixedParamName] = `${paramValue}`;
56
60
  break;
57
61
  case 'Body':
58
- body = paramValue;
62
+ if (typeof paramValue === 'string') {
63
+ try {
64
+ body = JSON.parse(paramValue);
65
+ }
66
+ catch (e) {
67
+ body = paramValue;
68
+ }
69
+ }
70
+ else {
71
+ body = paramValue;
72
+ }
59
73
  break;
60
74
  case 'Header':
61
75
  headers[fixedParamName] = `${paramValue}`;
@@ -63,7 +77,17 @@ export function registerGraphTools(server, graphClient, readOnly = false) {
63
77
  }
64
78
  }
65
79
  else if (paramName === 'body') {
66
- body = paramValue;
80
+ if (typeof paramValue === 'string') {
81
+ try {
82
+ body = JSON.parse(paramValue);
83
+ }
84
+ catch (e) {
85
+ body = paramValue;
86
+ }
87
+ }
88
+ else {
89
+ body = paramValue;
90
+ }
67
91
  logger.info(`Set legacy body param: ${JSON.stringify(body)}`);
68
92
  }
69
93
  }
@@ -78,7 +102,12 @@ export function registerGraphTools(server, graphClient, readOnly = false) {
78
102
  headers,
79
103
  };
80
104
  if (options.method !== 'GET' && body) {
81
- options.body = JSON.stringify(body);
105
+ options.body = typeof body === 'string' ? body : JSON.stringify(body);
106
+ }
107
+ const isProbablyMediaContent = tool.errors?.some((error) => error.description === 'Retrieved media content') ||
108
+ path.endsWith('/content');
109
+ if (isProbablyMediaContent) {
110
+ options.rawResponse = true;
82
111
  }
83
112
  logger.info(`Making graph request to ${path} with options: ${JSON.stringify(options)}`);
84
113
  const response = await graphClient.graphRequest(path, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Microsoft 365 MCP Server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -213,7 +213,7 @@
213
213
  ]
214
214
  },
215
215
  {
216
- "pathPattern": "/me/onenote/notebooks/{notebook-id}/sections/{onenoteSection-id}/pages",
216
+ "pathPattern": "/me/onenote/sections/{onenoteSection-id}/pages",
217
217
  "method": "get",
218
218
  "toolName": "list-onenote-section-pages",
219
219
  "scopes": [