ms365-mcp-server 1.1.4 → 1.1.5

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
@@ -60,7 +60,7 @@ function parseArgs() {
60
60
  }
61
61
  const server = new Server({
62
62
  name: "ms365-mcp-server",
63
- version: "1.1.4"
63
+ version: "1.1.5"
64
64
  }, {
65
65
  capabilities: {
66
66
  resources: {
@@ -1067,7 +1067,7 @@ function generateUniqueFilename(originalName) {
1067
1067
  const timestamp = Date.now();
1068
1068
  const randomString = crypto.randomBytes(8).toString('hex');
1069
1069
  const extension = path.extname(originalName);
1070
- const baseName = path.basename(originalName, extension);
1070
+ const baseName = path.basename(originalName, extension).replace(/[^a-zA-Z0-9-_]/g, '_');
1071
1071
  return `${baseName}-${timestamp}-${randomString}${extension}`;
1072
1072
  }
1073
1073
  // Helper function to save base64 content to file
@@ -810,33 +810,100 @@ export class MS365Operations {
810
810
  async searchEmailsToMe(additionalCriteria = {}) {
811
811
  try {
812
812
  const userEmail = await this.getCurrentUserEmail();
813
- // First search for emails where user is in TO field
814
- const toCriteria = {
815
- ...additionalCriteria,
816
- to: userEmail
817
- };
818
- // Then search for emails where user is in CC field
819
- const ccCriteria = {
820
- ...additionalCriteria,
821
- cc: userEmail
822
- };
823
- // Execute both searches in parallel
824
- const [toResults, ccResults] = await Promise.all([
825
- this.searchEmails(toCriteria),
826
- this.searchEmails(ccCriteria)
827
- ]);
828
- // Combine results and remove duplicates based on email ID
829
- const allMessages = [...toResults.messages, ...ccResults.messages];
830
- const uniqueMessages = allMessages.filter((message, index, array) => array.findIndex(m => m.id === message.id) === index);
831
- // Sort by received date (newest first)
832
- uniqueMessages.sort((a, b) => new Date(b.receivedDateTime).getTime() - new Date(a.receivedDateTime).getTime());
833
- // Apply maxResults limit if specified
834
- const maxResults = additionalCriteria.maxResults || 50;
835
- const limitedMessages = uniqueMessages.slice(0, maxResults);
836
- return {
837
- messages: limitedMessages,
838
- hasMore: uniqueMessages.length > maxResults || toResults.hasMore || ccResults.hasMore
839
- };
813
+ const graphClient = await this.getGraphClient();
814
+ // Create cache key from criteria
815
+ const cacheKey = JSON.stringify({ ...additionalCriteria, userEmail });
816
+ const cachedResults = this.getCachedResults(cacheKey);
817
+ if (cachedResults) {
818
+ return cachedResults;
819
+ }
820
+ try {
821
+ // Start with a basic query to get emails
822
+ const apiCall = graphClient.api('/me/messages')
823
+ .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,bodyPreview,isRead,hasAttachments,importance,conversationId,parentFolderId,webLink');
824
+ // Add search query for user's email in TO or CC using proper syntax
825
+ apiCall.search(`"${userEmail}"`);
826
+ // Add simple filters that are supported by the API
827
+ if (additionalCriteria.after) {
828
+ apiCall.filter(`receivedDateTime ge ${new Date(additionalCriteria.after).toISOString()}`);
829
+ }
830
+ if (additionalCriteria.before) {
831
+ apiCall.filter(`receivedDateTime le ${new Date(additionalCriteria.before).toISOString()}`);
832
+ }
833
+ if (additionalCriteria.hasAttachment !== undefined) {
834
+ apiCall.filter(`hasAttachments eq ${additionalCriteria.hasAttachment}`);
835
+ }
836
+ if (additionalCriteria.isUnread !== undefined) {
837
+ apiCall.filter(`isRead eq ${!additionalCriteria.isUnread}`);
838
+ }
839
+ if (additionalCriteria.importance) {
840
+ apiCall.filter(`importance eq '${additionalCriteria.importance}'`);
841
+ }
842
+ // Set page size
843
+ const pageSize = Math.min(additionalCriteria.maxResults || 100, 100);
844
+ apiCall.top(pageSize);
845
+ const result = await apiCall.get();
846
+ const messages = result.value?.map((email) => ({
847
+ id: email.id,
848
+ subject: email.subject || '',
849
+ from: {
850
+ name: email.from?.emailAddress?.name || '',
851
+ address: email.from?.emailAddress?.address || ''
852
+ },
853
+ toRecipients: email.toRecipients?.map((recipient) => ({
854
+ name: recipient.emailAddress?.name || '',
855
+ address: recipient.emailAddress?.address || ''
856
+ })) || [],
857
+ ccRecipients: email.ccRecipients?.map((recipient) => ({
858
+ name: recipient.emailAddress?.name || '',
859
+ address: recipient.emailAddress?.address || ''
860
+ })) || [],
861
+ receivedDateTime: email.receivedDateTime,
862
+ sentDateTime: email.sentDateTime,
863
+ bodyPreview: email.bodyPreview || '',
864
+ isRead: email.isRead || false,
865
+ hasAttachments: email.hasAttachments || false,
866
+ importance: email.importance || 'normal',
867
+ conversationId: email.conversationId || '',
868
+ parentFolderId: email.parentFolderId || '',
869
+ webLink: email.webLink || '',
870
+ attachments: []
871
+ })) || [];
872
+ // Filter messages to only include those where the user is in TO or CC
873
+ const filteredMessages = messages.filter(message => {
874
+ const isInTo = message.toRecipients.some(recipient => recipient.address.toLowerCase() === userEmail.toLowerCase());
875
+ const isInCc = message.ccRecipients.some(recipient => recipient.address.toLowerCase() === userEmail.toLowerCase());
876
+ return isInTo || isInCc;
877
+ });
878
+ // Sort messages by receivedDateTime in descending order
879
+ filteredMessages.sort((a, b) => new Date(b.receivedDateTime).getTime() - new Date(a.receivedDateTime).getTime());
880
+ // For emails with attachments, get attachment counts
881
+ for (const message of filteredMessages) {
882
+ if (message.hasAttachments) {
883
+ try {
884
+ const attachments = await graphClient
885
+ .api(`/me/messages/${message.id}/attachments`)
886
+ .select('id')
887
+ .get();
888
+ message.attachments = new Array(attachments.value?.length || 0);
889
+ }
890
+ catch (error) {
891
+ logger.error(`Error getting attachment count for message ${message.id}:`, error);
892
+ message.attachments = [];
893
+ }
894
+ }
895
+ }
896
+ const searchResult = {
897
+ messages: filteredMessages,
898
+ hasMore: !!result['@odata.nextLink']
899
+ };
900
+ this.setCachedResults(cacheKey, searchResult);
901
+ return searchResult;
902
+ }
903
+ catch (error) {
904
+ logger.error('Error in email search:', error);
905
+ throw error;
906
+ }
840
907
  }
841
908
  catch (error) {
842
909
  logger.error('Error searching emails addressed to me:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ms365-mcp-server",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
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",