msexchange-mcp
Version:
MCP server for Microsoft Exchange/Outlook email operations via Microsoft Graph API
167 lines (163 loc) • 8.51 kB
JavaScript
import { z } from 'zod';
import { MailService } from '../mail/mailService.js';
import accountsConfig from '../config/accountsLoader.js';
export const createMailRuleTool = {
name: 'create_mail_rule',
description: `Create an Outlook mail rule to automatically organize incoming emails.
Features:
- Multiple conditions: sender, subject, body keywords, attachments, importance
- Multiple actions: move/copy to folder, mark as read, forward, delete, categorize
- Rule priority with sequence ordering
- Can be enabled/disabled
Common use cases:
- Auto-file emails from specific senders to folders
- Forward urgent emails to team members
- Mark newsletters as read automatically
- Delete spam based on keywords
Examples:
- Move emails from boss to Important folder: {"conditions": {"from_addresses": ["boss@company.com"]}, "actions": {"move_to_folder": "Important_folder_id"}}
- Auto-categorize project emails: {"conditions": {"subject_contains": ["Project X"]}, "actions": {"add_categories": ["Project X", "Work"]}}
- Forward urgent emails: {"conditions": {"importance": "high", "to_me": true}, "actions": {"forward_to": "assistant@company.com"}}
Best practices:
- Test rules with non-destructive actions first
- Use specific conditions to avoid false positives
- Order rules by priority (lower sequence numbers run first)
- Disable rules when troubleshooting email issues`,
parameters: z.object({
user_id: z.string().describe('The user ID or email address'),
rule_name: z.string().describe('Name for the mail rule'),
conditions: z.object({
from_addresses: z.array(z.string()).optional().describe('Email addresses to match in From field'),
subject_contains: z.array(z.string()).optional().describe('Keywords that must be in subject'),
body_contains: z.array(z.string()).optional().describe('Keywords that must be in body'),
to_me: z.boolean().optional().describe('Email is addressed to me'),
cc_me: z.boolean().optional().describe('Email has me in CC'),
sent_to: z.array(z.string()).optional().describe('Email is sent to these addresses'),
importance: z.enum(['low', 'normal', 'high']).optional().describe('Message importance level'),
has_attachments: z.boolean().optional().describe('Email has attachments'),
}).describe('Conditions that trigger the rule'),
actions: z.object({
move_to_folder: z.string().optional().describe('Folder ID to move emails to'),
copy_to_folder: z.string().optional().describe('Folder ID to copy emails to'),
add_categories: z.array(z.string()).optional().describe('Categories to add to the email'),
mark_as_read: z.boolean().optional().describe('Mark email as read'),
mark_importance: z.enum(['low', 'normal', 'high']).optional().describe('Set importance level'),
forward_to: z.string().optional().describe('Email address to forward to'),
delete_message: z.boolean().optional().describe('Delete the message'),
stop_processing: z.boolean().optional().default(true).describe('Stop processing additional rules'),
}).describe('Actions to perform when conditions are met'),
enabled: z.boolean().optional().default(true).describe('Whether the rule is enabled'),
sequence: z.number().optional().describe('Rule execution order (lower numbers run first)'),
}),
execute: async (params) => {
try {
const account = accountsConfig.accounts.find(acc => acc.id === params.user_id || acc.email === params.user_id);
if (!account) {
throw new Error(`User not found: ${params.user_id}`);
}
const mailService = new MailService(account.email);
// Build conditions for Microsoft Graph API format
const graphConditions = {};
if (params.conditions.from_addresses?.length > 0) {
graphConditions.fromAddresses = params.conditions.from_addresses.map((addr) => ({
emailAddress: { address: addr }
}));
}
if (params.conditions.subject_contains?.length > 0) {
graphConditions.subjectContains = params.conditions.subject_contains;
}
if (params.conditions.body_contains?.length > 0) {
graphConditions.bodyContains = params.conditions.body_contains;
}
if (params.conditions.sent_to?.length > 0) {
graphConditions.recipientContains = params.conditions.sent_to.map((addr) => ({
emailAddress: { address: addr }
}));
}
if (params.conditions.importance) {
graphConditions.importance = params.conditions.importance;
}
if (params.conditions.has_attachments !== undefined) {
graphConditions.hasAttachments = params.conditions.has_attachments;
}
if (params.conditions.to_me) {
graphConditions.sentToMe = true;
}
if (params.conditions.cc_me) {
graphConditions.sentCcMe = true;
}
// Build actions for Microsoft Graph API format
const graphActions = {};
if (params.actions.move_to_folder) {
graphActions.moveToFolder = params.actions.move_to_folder;
}
if (params.actions.copy_to_folder) {
graphActions.copyToFolder = params.actions.copy_to_folder;
}
if (params.actions.add_categories?.length > 0) {
graphActions.assignCategories = params.actions.add_categories;
}
if (params.actions.mark_as_read !== undefined) {
graphActions.markAsRead = params.actions.mark_as_read;
}
if (params.actions.mark_importance) {
graphActions.markImportance = params.actions.mark_importance;
}
if (params.actions.forward_to) {
graphActions.forwardTo = {
emailAddress: { address: params.actions.forward_to }
};
}
if (params.actions.delete_message) {
graphActions.delete = true;
}
if (params.actions.stop_processing !== undefined) {
graphActions.stopProcessingRules = params.actions.stop_processing;
}
const ruleData = {
displayName: params.rule_name,
sequence: params.sequence || 1,
isEnabled: params.enabled !== false,
conditions: graphConditions,
actions: graphActions,
};
const newRule = await mailService.createMailRule(ruleData);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
rule: {
id: newRule.id,
displayName: newRule.displayName,
sequence: newRule.sequence,
isEnabled: newRule.isEnabled,
conditions: newRule.conditions,
actions: newRule.actions,
},
message: `Successfully created mail rule '${params.rule_name}'`,
usage_tips: [
'Test the rule with a few emails before enabling for all mail',
'Use lower sequence numbers for higher priority rules',
'Consider using "stop_processing: false" to allow multiple rules to apply',
'Check rule execution order if emails aren\'t being processed as expected'
]
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: 'text',
text: `Error creating mail rule: ${error instanceof Error ? error.message : 'Unknown error'}`,
},
],
};
}
},
};
//# sourceMappingURL=createMailRule.js.map