ms365-mcp-server 1.1.7 → 1.1.9

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/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
7
  import express from 'express';
8
+ import cors from 'cors';
8
9
  import path from 'path';
9
10
  import { fileURLToPath } from 'url';
10
11
  import fs from 'fs/promises';
@@ -66,7 +67,7 @@ function parseArgs() {
66
67
  }
67
68
  const server = new Server({
68
69
  name: "ms365-mcp-server",
69
- version: "1.1.7"
70
+ version: "1.1.9"
70
71
  }, {
71
72
  capabilities: {
72
73
  resources: {
@@ -686,12 +687,78 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
686
687
  };
687
688
  case "search_to_me":
688
689
  const searchToMeResults = await ms365Ops.searchEmailsToMe(args);
690
+ // Process attachments for each email
691
+ const processedEmails = await Promise.all(searchToMeResults.messages.map(async (email) => {
692
+ try {
693
+ // Get full email details including body
694
+ const fullEmail = await ms365Ops.getEmail(email.id, email.hasAttachments);
695
+ // Update email with body content
696
+ email.body = fullEmail.body || fullEmail.bodyPreview || '';
697
+ if (email.hasAttachments && fullEmail.attachments) {
698
+ // Process each attachment
699
+ const processedAttachments = await Promise.all(fullEmail.attachments.map(async (attachment) => {
700
+ try {
701
+ // Get the actual attachment content
702
+ const attachmentContent = await ms365Ops.getAttachment(email.id, attachment.id);
703
+ if (attachmentContent && attachmentContent.contentBytes) {
704
+ const savedFilename = await saveBase64ToFile(attachmentContent.contentBytes, attachment.name);
705
+ const fileUrl = `${SERVER_URL}/attachments/${savedFilename}`;
706
+ return {
707
+ ...attachment,
708
+ fileUrl: fileUrl
709
+ };
710
+ }
711
+ return attachment;
712
+ }
713
+ catch (error) {
714
+ logger.error(`Error processing attachment ${attachment.id}:`, error);
715
+ return attachment;
716
+ }
717
+ }));
718
+ email.attachments = processedAttachments;
719
+ email.artifacts = getListOfArtifacts("search_to_me", processedAttachments);
720
+ }
721
+ }
722
+ catch (error) {
723
+ logger.error(`Error processing email ${email.id}:`, error);
724
+ }
725
+ return email;
726
+ }));
689
727
  return {
690
728
  content: [
691
729
  {
692
730
  type: "text",
693
- text: `🔍 Emails Addressed to You (TO & CC) - ${searchToMeResults.messages.length} found\n\n${searchToMeResults.messages.map((email, index) => `${index + 1}. 📧 ${email.subject}\n 👤 From: ${email.from.name} <${email.from.address}>\n 📅 ${new Date(email.receivedDateTime).toLocaleDateString()}\n 🆔 ID: ${email.id}\n`).join('\n')}`
694
- }
731
+ text: `🔍 Emails Addressed to You (TO & CC) - ${processedEmails.length} found\n\n${processedEmails.map((email, index) => {
732
+ let emailText = `${index + 1}. 📧 ${email.subject}\n 👤 From: ${email.from.name} <${email.from.address}>\n 📅 ${new Date(email.receivedDateTime).toLocaleDateString()}\n 🆔 ID: ${email.id}\n`;
733
+ if (email.hasAttachments && email.attachments && email.attachments.length > 0) {
734
+ emailText += ` 📎 Attachments (${email.attachments.length}):\n`;
735
+ email.attachments.forEach((attachment, attIndex) => {
736
+ const sizeInMB = (attachment.size / (1024 * 1024)).toFixed(2);
737
+ emailText += ` ${attIndex + 1}. ${attachment.name}\n`;
738
+ emailText += ` 📦 Size: ${sizeInMB} MB\n`;
739
+ emailText += ` 📄 Type: ${attachment.contentType}\n`;
740
+ emailText += ` 🆔 ID: ${attachment.id}\n`;
741
+ if (attachment.fileUrl) {
742
+ emailText += ` 🔗 Download URL: ${attachment.fileUrl}\n`;
743
+ }
744
+ if (attachment.isInline) {
745
+ emailText += ` 📌 Inline: Yes\n`;
746
+ }
747
+ });
748
+ }
749
+ // Add email body with proper formatting
750
+ if (email.body) {
751
+ emailText += `\n 💬 Content:\n${email.body.split('\n').map((line) => ` ${line}`).join('\n')}\n`;
752
+ }
753
+ else {
754
+ emailText += `\n 💬 Content: No content available\n`;
755
+ }
756
+ return emailText;
757
+ }).join('\n')}`
758
+ },
759
+ ...processedEmails
760
+ .filter((email) => email.artifacts)
761
+ .flatMap((email) => email.artifacts)
695
762
  ]
696
763
  };
697
764
  case "list":
@@ -1059,9 +1126,16 @@ function getListOfArtifacts(functionName, attachments) {
1059
1126
  const app = express();
1060
1127
  const PORT = process.env.PORT || 55000;
1061
1128
  const SERVER_URL = ms365Config.serverUrl || `http://localhost:${PORT}`;
1129
+ // Enable CORS for all routes
1130
+ app.use(cors());
1062
1131
  // Get the directory name in ESM
1063
1132
  const __filename = fileURLToPath(import.meta.url);
1064
1133
  const __dirname = path.dirname(__filename);
1134
+ // Create public/attachments directory if it doesn't exist
1135
+ const attachmentsDir = path.join(__dirname, '../public/attachments');
1136
+ fs.mkdir(attachmentsDir, { recursive: true })
1137
+ .then(() => logger.log('Attachments directory ready'))
1138
+ .catch(err => logger.error('Error creating attachments directory:', err));
1065
1139
  // Serve static files from public directory
1066
1140
  app.use('/attachments', express.static(path.join(__dirname, '../public/attachments')));
1067
1141
  // Start Express server
@@ -860,9 +860,6 @@ export class MS365Operations {
860
860
  if (additionalCriteria.before) {
861
861
  apiCall.filter(`receivedDateTime le ${new Date(additionalCriteria.before).toISOString()}`);
862
862
  }
863
- if (additionalCriteria.hasAttachment !== undefined) {
864
- apiCall.filter(`hasAttachments eq ${additionalCriteria.hasAttachment}`);
865
- }
866
863
  if (additionalCriteria.isUnread !== undefined) {
867
864
  apiCall.filter(`isRead eq ${!additionalCriteria.isUnread}`);
868
865
  }
@@ -900,11 +897,15 @@ export class MS365Operations {
900
897
  attachments: []
901
898
  })) || [];
902
899
  // Filter messages to only include those where the user is in TO or CC
903
- const filteredMessages = messages.filter(message => {
900
+ let filteredMessages = messages.filter(message => {
904
901
  const isInTo = message.toRecipients.some(recipient => recipient.address.toLowerCase() === userEmail.toLowerCase());
905
902
  const isInCc = message.ccRecipients.some(recipient => recipient.address.toLowerCase() === userEmail.toLowerCase());
906
903
  return isInTo || isInCc;
907
904
  });
905
+ // Apply hasAttachment filter manually if specified
906
+ if (additionalCriteria.hasAttachment !== undefined) {
907
+ filteredMessages = filteredMessages.filter(message => message.hasAttachments === additionalCriteria.hasAttachment);
908
+ }
908
909
  // Sort messages by receivedDateTime in descending order
909
910
  filteredMessages.sort((a, b) => new Date(b.receivedDateTime).getTime() - new Date(a.receivedDateTime).getTime());
910
911
  // For emails with attachments, get attachment counts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ms365-mcp-server",
3
- "version": "1.1.7",
3
+ "version": "1.1.9",
4
4
  "description": "Microsoft 365 MCP Server for managing Microsoft 365 email through natural language interactions with full OAuth2 authentication support",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -33,6 +33,8 @@
33
33
  "@azure/msal-node": "^2.6.6",
34
34
  "@microsoft/microsoft-graph-client": "^3.0.7",
35
35
  "@modelcontextprotocol/sdk": "^1.10.1",
36
+ "@types/cors": "^2.8.19",
37
+ "cors": "^2.8.5",
36
38
  "express": "^5.1.0",
37
39
  "isomorphic-fetch": "^3.0.0",
38
40
  "keytar": "^7.9.0",