@softeria/ms-365-mcp-server 0.88.0 → 0.88.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.
package/README.md CHANGED
@@ -622,6 +622,10 @@ After cloning the repository, you may need to generate the client code from the
622
622
  npm run generate
623
623
  ```
624
624
 
625
+ ## Related Projects
626
+
627
+ - [ms-365-admin-mcp-server](https://github.com/okapi-ca/ms-365-admin-mcp-server) by [@okapi-ca](https://github.com/okapi-ca): companion server for admin/daemon scenarios using application permissions (client credentials flow), covering security alerts, audit logs, service health, and usage reports.
628
+
625
629
  ## Support
626
630
 
627
631
  If you're having problems or need help:
@@ -395,4 +395,50 @@ describe("graph-tools", () => {
395
395
  expect(tool.schema["timezone"]).toBeUndefined();
396
396
  });
397
397
  });
398
+ describe("outlook.body-content-type Prefer header", () => {
399
+ it('should set Prefer: outlook.body-content-type="text" on GET requests', async () => {
400
+ const endpoint = makeEndpoint({ method: "get" });
401
+ const config = makeConfig({ method: "get" });
402
+ mockEndpoints.push(endpoint);
403
+ mockEndpointsJson = [config];
404
+ const graphClient = createMockGraphClient([
405
+ { content: [{ type: "text", text: JSON.stringify({ value: [] }) }] }
406
+ ]);
407
+ const server = createMockServer();
408
+ const { registerGraphTools } = await loadModule();
409
+ registerGraphTools(server, graphClient);
410
+ await server.tools.get("test-tool").handler({});
411
+ const [, options] = graphClient.graphRequest.mock.calls[0];
412
+ expect(options.headers["Prefer"]).toContain('outlook.body-content-type="text"');
413
+ });
414
+ it("should NOT set Prefer: outlook.body-content-type on POST requests", async () => {
415
+ const endpoint = makeEndpoint({
416
+ alias: "create-reply-draft",
417
+ method: "post",
418
+ path: "/me/messages/:messageId/createReply",
419
+ parameters: [
420
+ { name: "messageId", type: "Path", schema: z.string() },
421
+ { name: "body", type: "Body", schema: z.any() }
422
+ ]
423
+ });
424
+ const config = makeConfig({
425
+ toolName: "create-reply-draft",
426
+ method: "post",
427
+ pathPattern: "/me/messages/{message-id}/createReply"
428
+ });
429
+ mockEndpoints.push(endpoint);
430
+ mockEndpointsJson = [config];
431
+ const graphClient = createMockGraphClient([{ content: [{ type: "text", text: "{}" }] }]);
432
+ const server = createMockServer();
433
+ const { registerGraphTools } = await loadModule();
434
+ registerGraphTools(server, graphClient);
435
+ await server.tools.get("create-reply-draft").handler({
436
+ messageId: "AAMk123",
437
+ body: { Message: { body: { contentType: "html", content: "<p>hi</p>" } } }
438
+ });
439
+ const [, options] = graphClient.graphRequest.mock.calls[0];
440
+ const prefer = options.headers["Prefer"];
441
+ expect(prefer === void 0 || !prefer.includes("outlook.body-content-type")).toBe(true);
442
+ });
443
+ });
398
444
  });
@@ -190,13 +190,15 @@
190
190
  "pathPattern": "/me/messages/{message-id}/createReply",
191
191
  "method": "post",
192
192
  "toolName": "create-reply-draft",
193
- "scopes": ["Mail.ReadWrite"]
193
+ "scopes": ["Mail.ReadWrite"],
194
+ "llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
194
195
  },
195
196
  {
196
197
  "pathPattern": "/me/messages/{message-id}/createReplyAll",
197
198
  "method": "post",
198
199
  "toolName": "create-reply-all-draft",
199
- "scopes": ["Mail.ReadWrite"]
200
+ "scopes": ["Mail.ReadWrite"],
201
+ "llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
200
202
  },
201
203
  {
202
204
  "pathPattern": "/me/messages/{message-id}/send",
@@ -142,7 +142,7 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
142
142
  logger.info(`Setting timezone preference: outlook.timezone="${params.timezone}"`);
143
143
  }
144
144
  const bodyFormat = process.env.MS365_MCP_BODY_FORMAT || "text";
145
- if (bodyFormat !== "html") {
145
+ if (bodyFormat !== "html" && tool.method.toUpperCase() === "GET") {
146
146
  preferValues.push(`outlook.body-content-type="${bodyFormat}"`);
147
147
  }
148
148
  if (preferValues.length > 0) {
@@ -221,13 +221,8 @@ async function executeGraphTool(tool, config, graphClient, params, authManager)
221
221
  while (nextLink && pageCount < maxPages && allItems.length < maxItems) {
222
222
  logger.info(`Fetching page ${pageCount + 1} from: ${nextLink}`);
223
223
  const url = new URL(nextLink);
224
- const nextPath = url.pathname.replace("/v1.0", "");
224
+ const nextPath = url.pathname.replace("/v1.0", "") + url.search;
225
225
  const nextOptions = { ...options };
226
- const nextQueryParams = {};
227
- for (const [key, value] of url.searchParams.entries()) {
228
- nextQueryParams[key] = value;
229
- }
230
- nextOptions.queryParams = nextQueryParams;
231
226
  const nextResponse = await graphClient.graphRequest(nextPath, nextOptions);
232
227
  if (nextResponse?.content?.[0]?.text) {
233
228
  const nextJsonResponse = JSON.parse(nextResponse.content[0].text);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softeria/ms-365-mcp-server",
3
- "version": "0.88.0",
3
+ "version": "0.88.2",
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",
@@ -190,13 +190,15 @@
190
190
  "pathPattern": "/me/messages/{message-id}/createReply",
191
191
  "method": "post",
192
192
  "toolName": "create-reply-draft",
193
- "scopes": ["Mail.ReadWrite"]
193
+ "scopes": ["Mail.ReadWrite"],
194
+ "llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
194
195
  },
195
196
  {
196
197
  "pathPattern": "/me/messages/{message-id}/createReplyAll",
197
198
  "method": "post",
198
199
  "toolName": "create-reply-all-draft",
199
- "scopes": ["Mail.ReadWrite"]
200
+ "scopes": ["Mail.ReadWrite"],
201
+ "llmTip": "For HTML replies pass Message.body.contentType: 'html' with Message.body.content as HTML. Note: supplying Message.body replaces the whole draft body, so the original quoted history is not included. Specifying both 'comment' and Message.body returns 400. Signatures are added by the Outlook client only, not via Graph."
200
202
  },
201
203
  {
202
204
  "pathPattern": "/me/messages/{message-id}/send",