@tgai96/outlook-mcp 1.0.0
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 +396 -0
- package/auth/index.js +64 -0
- package/auth/oauth-server.js +178 -0
- package/auth/token-manager.js +139 -0
- package/auth/token-storage.js +317 -0
- package/auth/tools.js +171 -0
- package/calendar/accept.js +64 -0
- package/calendar/cancel.js +64 -0
- package/calendar/create.js +69 -0
- package/calendar/decline.js +64 -0
- package/calendar/delete.js +59 -0
- package/calendar/index.js +123 -0
- package/calendar/list.js +77 -0
- package/cli.js +246 -0
- package/config.js +108 -0
- package/email/folder-utils.js +175 -0
- package/email/index.js +157 -0
- package/email/list.js +78 -0
- package/email/mark-as-read.js +101 -0
- package/email/read.js +128 -0
- package/email/search.js +285 -0
- package/email/send.js +120 -0
- package/folder/create.js +124 -0
- package/folder/index.js +78 -0
- package/folder/list.js +264 -0
- package/folder/move.js +163 -0
- package/index.js +148 -0
- package/package.json +54 -0
- package/rules/create.js +248 -0
- package/rules/index.js +175 -0
- package/rules/list.js +202 -0
- package/utils/graph-api.js +192 -0
- package/utils/mock-data.js +145 -0
- package/utils/odata-helpers.js +40 -0
package/rules/create.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create rule functionality
|
|
3
|
+
*/
|
|
4
|
+
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
|
+
const { ensureAuthenticated } = require('../auth');
|
|
6
|
+
const { getFolderIdByName } = require('../email/folder-utils');
|
|
7
|
+
const { getInboxRules } = require('./list');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create rule handler
|
|
11
|
+
* @param {object} args - Tool arguments
|
|
12
|
+
* @returns {object} - MCP response
|
|
13
|
+
*/
|
|
14
|
+
async function handleCreateRule(args) {
|
|
15
|
+
const {
|
|
16
|
+
name,
|
|
17
|
+
fromAddresses,
|
|
18
|
+
containsSubject,
|
|
19
|
+
hasAttachments,
|
|
20
|
+
moveToFolder,
|
|
21
|
+
markAsRead,
|
|
22
|
+
isEnabled = true,
|
|
23
|
+
sequence
|
|
24
|
+
} = args;
|
|
25
|
+
|
|
26
|
+
// Add validation for sequence parameter
|
|
27
|
+
if (sequence !== undefined && (isNaN(sequence) || sequence < 1)) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: "Sequence must be a positive number greater than zero."
|
|
32
|
+
}]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!name) {
|
|
37
|
+
return {
|
|
38
|
+
content: [{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: "Rule name is required."
|
|
41
|
+
}]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Validate that at least one condition or action is specified
|
|
46
|
+
const hasCondition = fromAddresses || containsSubject || hasAttachments === true;
|
|
47
|
+
const hasAction = moveToFolder || markAsRead === true;
|
|
48
|
+
|
|
49
|
+
if (!hasCondition) {
|
|
50
|
+
return {
|
|
51
|
+
content: [{
|
|
52
|
+
type: "text",
|
|
53
|
+
text: "At least one condition is required. Specify fromAddresses, containsSubject, or hasAttachments."
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!hasAction) {
|
|
59
|
+
return {
|
|
60
|
+
content: [{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: "At least one action is required. Specify moveToFolder or markAsRead."
|
|
63
|
+
}]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
// Get access token
|
|
69
|
+
const accessToken = await ensureAuthenticated();
|
|
70
|
+
|
|
71
|
+
// Create rule
|
|
72
|
+
const result = await createInboxRule(accessToken, {
|
|
73
|
+
name,
|
|
74
|
+
fromAddresses,
|
|
75
|
+
containsSubject,
|
|
76
|
+
hasAttachments,
|
|
77
|
+
moveToFolder,
|
|
78
|
+
markAsRead,
|
|
79
|
+
isEnabled,
|
|
80
|
+
sequence
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
let responseText = result.message;
|
|
84
|
+
|
|
85
|
+
// Add a tip about sequence if it wasn't provided
|
|
86
|
+
if (!sequence && !result.error) {
|
|
87
|
+
responseText += "\n\nTip: You can specify a 'sequence' parameter when creating rules to control their execution order. Lower sequence numbers run first.";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
content: [{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: responseText
|
|
94
|
+
}]
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (error.message === 'Authentication required') {
|
|
98
|
+
return {
|
|
99
|
+
content: [{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: "Authentication required. Please use the 'authenticate' tool first."
|
|
102
|
+
}]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
content: [{
|
|
108
|
+
type: "text",
|
|
109
|
+
text: `Error creating rule: ${error.message}`
|
|
110
|
+
}]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a new inbox rule
|
|
117
|
+
* @param {string} accessToken - Access token
|
|
118
|
+
* @param {object} ruleOptions - Rule creation options
|
|
119
|
+
* @returns {Promise<object>} - Result object with status and message
|
|
120
|
+
*/
|
|
121
|
+
async function createInboxRule(accessToken, ruleOptions) {
|
|
122
|
+
try {
|
|
123
|
+
const {
|
|
124
|
+
name,
|
|
125
|
+
fromAddresses,
|
|
126
|
+
containsSubject,
|
|
127
|
+
hasAttachments,
|
|
128
|
+
moveToFolder,
|
|
129
|
+
markAsRead,
|
|
130
|
+
isEnabled,
|
|
131
|
+
sequence
|
|
132
|
+
} = ruleOptions;
|
|
133
|
+
|
|
134
|
+
// Get existing rules to determine sequence if not provided
|
|
135
|
+
let ruleSequence = sequence;
|
|
136
|
+
if (!ruleSequence) {
|
|
137
|
+
try {
|
|
138
|
+
// Default to 100 if we can't get existing rules
|
|
139
|
+
ruleSequence = 100;
|
|
140
|
+
|
|
141
|
+
// Get existing rules to find highest sequence
|
|
142
|
+
const existingRules = await getInboxRules(accessToken);
|
|
143
|
+
if (existingRules && existingRules.length > 0) {
|
|
144
|
+
// Find the highest sequence
|
|
145
|
+
const highestSequence = Math.max(...existingRules.map(r => r.sequence || 0));
|
|
146
|
+
// Set new rule sequence to be higher
|
|
147
|
+
ruleSequence = Math.max(highestSequence + 1, 100);
|
|
148
|
+
console.error(`Auto-generated sequence: ${ruleSequence} (based on highest existing: ${highestSequence})`);
|
|
149
|
+
}
|
|
150
|
+
} catch (sequenceError) {
|
|
151
|
+
console.error(`Error determining rule sequence: ${sequenceError.message}`);
|
|
152
|
+
// Fall back to default value
|
|
153
|
+
ruleSequence = 100;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.error(`Using rule sequence: ${ruleSequence}`);
|
|
158
|
+
|
|
159
|
+
// Make sure sequence is a positive integer
|
|
160
|
+
ruleSequence = Math.max(1, Math.floor(ruleSequence));
|
|
161
|
+
|
|
162
|
+
// Build rule object with sequence
|
|
163
|
+
const rule = {
|
|
164
|
+
displayName: name,
|
|
165
|
+
isEnabled: isEnabled === true,
|
|
166
|
+
sequence: ruleSequence,
|
|
167
|
+
conditions: {},
|
|
168
|
+
actions: {}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Add conditions
|
|
172
|
+
if (fromAddresses) {
|
|
173
|
+
// Parse email addresses
|
|
174
|
+
const emailAddresses = fromAddresses.split(',')
|
|
175
|
+
.map(email => email.trim())
|
|
176
|
+
.filter(email => email)
|
|
177
|
+
.map(email => ({
|
|
178
|
+
emailAddress: {
|
|
179
|
+
address: email
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
if (emailAddresses.length > 0) {
|
|
184
|
+
rule.conditions.fromAddresses = emailAddresses;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (containsSubject) {
|
|
189
|
+
rule.conditions.subjectContains = [containsSubject];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (hasAttachments === true) {
|
|
193
|
+
rule.conditions.hasAttachment = true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add actions
|
|
197
|
+
if (moveToFolder) {
|
|
198
|
+
// Get folder ID
|
|
199
|
+
try {
|
|
200
|
+
const folderId = await getFolderIdByName(accessToken, moveToFolder);
|
|
201
|
+
if (!folderId) {
|
|
202
|
+
return {
|
|
203
|
+
success: false,
|
|
204
|
+
message: `Target folder "${moveToFolder}" not found. Please specify a valid folder name.`
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
rule.actions.moveToFolder = folderId;
|
|
209
|
+
} catch (folderError) {
|
|
210
|
+
console.error(`Error resolving folder "${moveToFolder}": ${folderError.message}`);
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
message: `Error resolving folder "${moveToFolder}": ${folderError.message}`
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (markAsRead === true) {
|
|
219
|
+
rule.actions.markAsRead = true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Create the rule
|
|
223
|
+
const response = await callGraphAPI(
|
|
224
|
+
accessToken,
|
|
225
|
+
'POST',
|
|
226
|
+
'me/mailFolders/inbox/messageRules',
|
|
227
|
+
rule
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (response && response.id) {
|
|
231
|
+
return {
|
|
232
|
+
success: true,
|
|
233
|
+
message: `Successfully created rule "${name}" with sequence ${ruleSequence}.`,
|
|
234
|
+
ruleId: response.id
|
|
235
|
+
};
|
|
236
|
+
} else {
|
|
237
|
+
return {
|
|
238
|
+
success: false,
|
|
239
|
+
message: "Failed to create rule. The server didn't return a rule ID."
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error(`Error creating rule: ${error.message}`);
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
module.exports = handleCreateRule;
|
package/rules/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email rules management module for Outlook MCP server
|
|
3
|
+
*/
|
|
4
|
+
const handleListRules = require('./list');
|
|
5
|
+
const handleCreateRule = require('./create');
|
|
6
|
+
|
|
7
|
+
// Import getInboxRules for the edit sequence tool
|
|
8
|
+
const { getInboxRules } = require('./list');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Edit rule sequence handler
|
|
12
|
+
* @param {object} args - Tool arguments
|
|
13
|
+
* @returns {object} - MCP response
|
|
14
|
+
*/
|
|
15
|
+
async function handleEditRuleSequence(args) {
|
|
16
|
+
const { ruleName, sequence } = args;
|
|
17
|
+
|
|
18
|
+
if (!ruleName) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: "Rule name is required. Please specify the exact name of an existing rule."
|
|
23
|
+
}]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!sequence || isNaN(sequence) || sequence < 1) {
|
|
28
|
+
return {
|
|
29
|
+
content: [{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: "A positive sequence number is required. Lower numbers run first (higher priority)."
|
|
32
|
+
}]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Get access token
|
|
38
|
+
const accessToken = await ensureAuthenticated();
|
|
39
|
+
|
|
40
|
+
// Get all rules
|
|
41
|
+
const rules = await getInboxRules(accessToken);
|
|
42
|
+
|
|
43
|
+
// Find the rule by name
|
|
44
|
+
const rule = rules.find(r => r.displayName === ruleName);
|
|
45
|
+
if (!rule) {
|
|
46
|
+
return {
|
|
47
|
+
content: [{
|
|
48
|
+
type: "text",
|
|
49
|
+
text: `Rule with name "${ruleName}" not found.`
|
|
50
|
+
}]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Update the rule sequence
|
|
55
|
+
const updateResult = await callGraphAPI(
|
|
56
|
+
accessToken,
|
|
57
|
+
'PATCH',
|
|
58
|
+
`me/mailFolders/inbox/messageRules/${rule.id}`,
|
|
59
|
+
{
|
|
60
|
+
sequence: sequence
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: `Successfully updated the sequence of rule "${ruleName}" to ${sequence}.`
|
|
68
|
+
}]
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error.message === 'Authentication required') {
|
|
72
|
+
return {
|
|
73
|
+
content: [{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: "Authentication required. Please use the 'authenticate' tool first."
|
|
76
|
+
}]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
content: [{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: `Error updating rule sequence: ${error.message}`
|
|
84
|
+
}]
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Rules management tool definitions
|
|
90
|
+
const rulesTools = [
|
|
91
|
+
{
|
|
92
|
+
name: "list-rules",
|
|
93
|
+
description: "Lists inbox rules in your Outlook account",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
includeDetails: {
|
|
98
|
+
type: "boolean",
|
|
99
|
+
description: "Include detailed rule conditions and actions"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
required: []
|
|
103
|
+
},
|
|
104
|
+
handler: handleListRules
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: "create-rule",
|
|
108
|
+
description: "Creates a new inbox rule",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
name: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Name of the rule to create"
|
|
115
|
+
},
|
|
116
|
+
fromAddresses: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Comma-separated list of sender email addresses for the rule"
|
|
119
|
+
},
|
|
120
|
+
containsSubject: {
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Subject text the email must contain"
|
|
123
|
+
},
|
|
124
|
+
hasAttachments: {
|
|
125
|
+
type: "boolean",
|
|
126
|
+
description: "Whether the rule applies to emails with attachments"
|
|
127
|
+
},
|
|
128
|
+
moveToFolder: {
|
|
129
|
+
type: "string",
|
|
130
|
+
description: "Name of the folder to move matching emails to"
|
|
131
|
+
},
|
|
132
|
+
markAsRead: {
|
|
133
|
+
type: "boolean",
|
|
134
|
+
description: "Whether to mark matching emails as read"
|
|
135
|
+
},
|
|
136
|
+
isEnabled: {
|
|
137
|
+
type: "boolean",
|
|
138
|
+
description: "Whether the rule should be enabled after creation (default: true)"
|
|
139
|
+
},
|
|
140
|
+
sequence: {
|
|
141
|
+
type: "number",
|
|
142
|
+
description: "Order in which the rule is executed (lower numbers run first, default: 100)"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
required: ["name"]
|
|
146
|
+
},
|
|
147
|
+
handler: handleCreateRule
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: "edit-rule-sequence",
|
|
151
|
+
description: "Changes the execution order of an existing inbox rule",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
ruleName: {
|
|
156
|
+
type: "string",
|
|
157
|
+
description: "Name of the rule to modify"
|
|
158
|
+
},
|
|
159
|
+
sequence: {
|
|
160
|
+
type: "number",
|
|
161
|
+
description: "New sequence value for the rule (lower numbers run first)"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
required: ["ruleName", "sequence"]
|
|
165
|
+
},
|
|
166
|
+
handler: handleEditRuleSequence
|
|
167
|
+
}
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
rulesTools,
|
|
172
|
+
handleListRules,
|
|
173
|
+
handleCreateRule,
|
|
174
|
+
handleEditRuleSequence
|
|
175
|
+
};
|
package/rules/list.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List rules functionality
|
|
3
|
+
*/
|
|
4
|
+
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
|
+
const { ensureAuthenticated } = require('../auth');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* List rules handler
|
|
9
|
+
* @param {object} args - Tool arguments
|
|
10
|
+
* @returns {object} - MCP response
|
|
11
|
+
*/
|
|
12
|
+
async function handleListRules(args) {
|
|
13
|
+
const includeDetails = args.includeDetails === true;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
// Get access token
|
|
17
|
+
const accessToken = await ensureAuthenticated();
|
|
18
|
+
|
|
19
|
+
// Get all inbox rules
|
|
20
|
+
const rules = await getInboxRules(accessToken);
|
|
21
|
+
|
|
22
|
+
// Format the rules based on detail level
|
|
23
|
+
const formattedRules = formatRulesList(rules, includeDetails);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
content: [{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: formattedRules
|
|
29
|
+
}]
|
|
30
|
+
};
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (error.message === 'Authentication required') {
|
|
33
|
+
return {
|
|
34
|
+
content: [{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: "Authentication required. Please use the 'authenticate' tool first."
|
|
37
|
+
}]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
content: [{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: `Error listing rules: ${error.message}`
|
|
45
|
+
}]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get all inbox rules
|
|
52
|
+
* @param {string} accessToken - Access token
|
|
53
|
+
* @returns {Promise<Array>} - Array of rule objects
|
|
54
|
+
*/
|
|
55
|
+
async function getInboxRules(accessToken) {
|
|
56
|
+
try {
|
|
57
|
+
const response = await callGraphAPI(
|
|
58
|
+
accessToken,
|
|
59
|
+
'GET',
|
|
60
|
+
'me/mailFolders/inbox/messageRules',
|
|
61
|
+
null
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return response.value || [];
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(`Error getting inbox rules: ${error.message}`);
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Format rules list for display
|
|
73
|
+
* @param {Array} rules - Array of rule objects
|
|
74
|
+
* @param {boolean} includeDetails - Whether to include detailed conditions and actions
|
|
75
|
+
* @returns {string} - Formatted rules list
|
|
76
|
+
*/
|
|
77
|
+
function formatRulesList(rules, includeDetails) {
|
|
78
|
+
if (!rules || rules.length === 0) {
|
|
79
|
+
return "No inbox rules found.\n\nTip: You can create rules using the 'create-rule' tool. Rules are processed in order of their sequence number (lower numbers are processed first).";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Sort rules by sequence to show execution order
|
|
83
|
+
const sortedRules = [...rules].sort((a, b) => {
|
|
84
|
+
return (a.sequence || 9999) - (b.sequence || 9999);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Format rules based on detail level
|
|
88
|
+
if (includeDetails) {
|
|
89
|
+
// Detailed format
|
|
90
|
+
const detailedRules = sortedRules.map((rule, index) => {
|
|
91
|
+
// Format rule header with sequence
|
|
92
|
+
let ruleText = `${index + 1}. ${rule.displayName}${rule.isEnabled ? '' : ' (Disabled)'} - Sequence: ${rule.sequence || 'N/A'}`;
|
|
93
|
+
|
|
94
|
+
// Format conditions
|
|
95
|
+
const conditions = formatRuleConditions(rule);
|
|
96
|
+
if (conditions) {
|
|
97
|
+
ruleText += `\n Conditions: ${conditions}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Format actions
|
|
101
|
+
const actions = formatRuleActions(rule);
|
|
102
|
+
if (actions) {
|
|
103
|
+
ruleText += `\n Actions: ${actions}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return ruleText;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return `Found ${rules.length} inbox rules (sorted by execution order):\n\n${detailedRules.join('\n\n')}\n\nRules are processed in order of their sequence number. You can change rule order using the 'edit-rule-sequence' tool.`;
|
|
110
|
+
} else {
|
|
111
|
+
// Simple format
|
|
112
|
+
const simpleRules = sortedRules.map((rule, index) => {
|
|
113
|
+
return `${index + 1}. ${rule.displayName}${rule.isEnabled ? '' : ' (Disabled)'} - Sequence: ${rule.sequence || 'N/A'}`;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return `Found ${rules.length} inbox rules (sorted by execution order):\n\n${simpleRules.join('\n')}\n\nTip: Use 'list-rules with includeDetails=true' to see more information about each rule.`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Format rule conditions for display
|
|
122
|
+
* @param {object} rule - Rule object
|
|
123
|
+
* @returns {string} - Formatted conditions
|
|
124
|
+
*/
|
|
125
|
+
function formatRuleConditions(rule) {
|
|
126
|
+
const conditions = [];
|
|
127
|
+
|
|
128
|
+
// From addresses
|
|
129
|
+
if (rule.conditions?.fromAddresses?.length > 0) {
|
|
130
|
+
const senders = rule.conditions.fromAddresses.map(addr => addr.emailAddress.address).join(', ');
|
|
131
|
+
conditions.push(`From: ${senders}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Subject contains
|
|
135
|
+
if (rule.conditions?.subjectContains?.length > 0) {
|
|
136
|
+
conditions.push(`Subject contains: "${rule.conditions.subjectContains.join(', ')}"`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Contains body text
|
|
140
|
+
if (rule.conditions?.bodyContains?.length > 0) {
|
|
141
|
+
conditions.push(`Body contains: "${rule.conditions.bodyContains.join(', ')}"`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Has attachment
|
|
145
|
+
if (rule.conditions?.hasAttachment === true) {
|
|
146
|
+
conditions.push('Has attachment');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Importance
|
|
150
|
+
if (rule.conditions?.importance) {
|
|
151
|
+
conditions.push(`Importance: ${rule.conditions.importance}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return conditions.join('; ');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Format rule actions for display
|
|
159
|
+
* @param {object} rule - Rule object
|
|
160
|
+
* @returns {string} - Formatted actions
|
|
161
|
+
*/
|
|
162
|
+
function formatRuleActions(rule) {
|
|
163
|
+
const actions = [];
|
|
164
|
+
|
|
165
|
+
// Move to folder
|
|
166
|
+
if (rule.actions?.moveToFolder) {
|
|
167
|
+
actions.push(`Move to folder: ${rule.actions.moveToFolder}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Copy to folder
|
|
171
|
+
if (rule.actions?.copyToFolder) {
|
|
172
|
+
actions.push(`Copy to folder: ${rule.actions.copyToFolder}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Mark as read
|
|
176
|
+
if (rule.actions?.markAsRead === true) {
|
|
177
|
+
actions.push('Mark as read');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Mark importance
|
|
181
|
+
if (rule.actions?.markImportance) {
|
|
182
|
+
actions.push(`Mark importance: ${rule.actions.markImportance}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Forward
|
|
186
|
+
if (rule.actions?.forwardTo?.length > 0) {
|
|
187
|
+
const recipients = rule.actions.forwardTo.map(r => r.emailAddress.address).join(', ');
|
|
188
|
+
actions.push(`Forward to: ${recipients}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Delete
|
|
192
|
+
if (rule.actions?.delete === true) {
|
|
193
|
+
actions.push('Delete');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return actions.join('; ');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
handleListRules,
|
|
201
|
+
getInboxRules
|
|
202
|
+
};
|