email-smtp-imap-mcp 2.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/.env.example +48 -0
- package/README.md +87 -0
- package/build/accountManager.js +66 -0
- package/build/config.js +238 -0
- package/build/emailHandlers.js +175 -0
- package/build/emailService.js +238 -0
- package/build/emailTools.js +267 -0
- package/build/imapService.js +208 -0
- package/build/index.js +119 -0
- package/build/requestHandler.js +481 -0
- package/build/smtpService.js +142 -0
- package/build/tools.js +362 -0
- package/build/types.js +4 -0
- package/package.json +62 -0
package/.env.example
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Email MCP Server Configuration
|
|
2
|
+
|
|
3
|
+
# ===== OPTION 1: Multiple Accounts (JSON) =====
|
|
4
|
+
# Configure multiple email accounts in JSON format
|
|
5
|
+
EMAIL_ACCOUNTS_JSON='{
|
|
6
|
+
"work": {
|
|
7
|
+
"smtp_host": "smtp.mail.me.com",
|
|
8
|
+
"smtp_port": 587,
|
|
9
|
+
"smtp_secure": false,
|
|
10
|
+
"smtp_user": "your-email@icloud.com",
|
|
11
|
+
"smtp_pass": "your-app-specific-password",
|
|
12
|
+
"imap_host": "imap.mail.me.com",
|
|
13
|
+
"imap_port": 993,
|
|
14
|
+
"imap_secure": true,
|
|
15
|
+
"default_from_name": "Your Name"
|
|
16
|
+
},
|
|
17
|
+
"personal": {
|
|
18
|
+
"smtp_host": "smtp.gmail.com",
|
|
19
|
+
"smtp_port": 587,
|
|
20
|
+
"smtp_secure": false,
|
|
21
|
+
"smtp_user": "your-email@gmail.com",
|
|
22
|
+
"smtp_pass": "your-app-specific-password",
|
|
23
|
+
"imap_host": "imap.gmail.com",
|
|
24
|
+
"imap_port": 993,
|
|
25
|
+
"imap_secure": true,
|
|
26
|
+
"default_from_name": "Your Name"
|
|
27
|
+
}
|
|
28
|
+
}'
|
|
29
|
+
|
|
30
|
+
# Default account to use if not specified in tool calls
|
|
31
|
+
DEFAULT_EMAIL_ACCOUNT="work"
|
|
32
|
+
|
|
33
|
+
# ===== OPTION 2: Single Account (Individual Variables) =====
|
|
34
|
+
# Use these if you only have one email account
|
|
35
|
+
# SMTP_HOST="smtp.mail.me.com"
|
|
36
|
+
# SMTP_PORT=587
|
|
37
|
+
# SMTP_SECURE=false
|
|
38
|
+
# SMTP_USER="your-email@icloud.com"
|
|
39
|
+
# SMTP_PASS="your-app-specific-password"
|
|
40
|
+
# IMAP_HOST="imap.mail.me.com"
|
|
41
|
+
# IMAP_PORT=993
|
|
42
|
+
# IMAP_SECURE=true
|
|
43
|
+
# DEFAULT_FROM_NAME="Your Name"
|
|
44
|
+
|
|
45
|
+
# ===== NOTES =====
|
|
46
|
+
# For Gmail: Use App-Specific Passwords (not your regular password)
|
|
47
|
+
# For iCloud: Use App-Specific Passwords
|
|
48
|
+
# For other providers: Check their SMTP/IMAP settings documentation
|
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Email MCP Server
|
|
2
|
+
|
|
3
|
+
A clean, simple MCP server for email operations (SMTP + IMAP).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Send emails** (HTML + attachments)
|
|
8
|
+
- ✅ **Search emails** (flexible filters)
|
|
9
|
+
- ✅ **Reply/Forward** (with threading)
|
|
10
|
+
- ✅ **Organize** (mark read, archive, flag)
|
|
11
|
+
- ✅ **List folders**
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Configure
|
|
23
|
+
|
|
24
|
+
Create `.env`:
|
|
25
|
+
|
|
26
|
+
```env
|
|
27
|
+
EMAIL_ACCOUNTS_JSON={"icloud":{"smtp":{"host":"smtp.mail.me.com","port":587,"user":"your@icloud.com","password":"your-app-password"},"imap":{"host":"imap.mail.me.com","port":993},"default_from_name":"Your Name","sender_emails":["your@icloud.com","alias@domain.com"]}}
|
|
28
|
+
DEFAULT_EMAIL_ACCOUNT=icloud
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. Add to MCP-Supported Software
|
|
32
|
+
|
|
33
|
+
#### Claude Desktop
|
|
34
|
+
|
|
35
|
+
Add to your `claude_desktop_config.json`:
|
|
36
|
+
|
|
37
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
38
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"email": {
|
|
44
|
+
"command": "node",
|
|
45
|
+
"args": ["/absolute/path/to/email-smtp-imap-mcp/build/index.js"],
|
|
46
|
+
"env": {
|
|
47
|
+
"EMAIL_ACCOUNTS_JSON": "{\"icloud\":{\"smtp\":{\"host\":\"smtp.mail.me.com\",\"port\":587,\"user\":\"your@icloud.com\",\"password\":\"your-app-password\"},\"imap\":{\"host\":\"imap.mail.me.com\",\"port\":993},\"default_from_name\":\"Your Name\",\"sender_emails\":[\"your@icloud.com\"]}}",
|
|
48
|
+
"DEFAULT_EMAIL_ACCOUNT": "icloud"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Note**: Replace `/absolute/path/to/email-smtp-imap-mcp` with your actual installation path. You can also use a `.env` file instead of inline env config.
|
|
56
|
+
|
|
57
|
+
Restart Claude Desktop to load the MCP server.
|
|
58
|
+
|
|
59
|
+
## 5 Tools
|
|
60
|
+
|
|
61
|
+
| Tool | Purpose |
|
|
62
|
+
|------|---------|
|
|
63
|
+
| `emails_find` | Search emails with filters |
|
|
64
|
+
| `emails_modify` | Mark read, archive, flag |
|
|
65
|
+
| `email_send` | Send new emails |
|
|
66
|
+
| `email_respond` | Reply or forward |
|
|
67
|
+
| `folders_list` | List folders |
|
|
68
|
+
|
|
69
|
+
## Usage Examples
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
"Find unread emails from last week"
|
|
73
|
+
"Send an email to team@company.com"
|
|
74
|
+
"Reply to the last email from John"
|
|
75
|
+
"Archive all emails older than 30 days"
|
|
76
|
+
"List my email folders"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Documentation
|
|
80
|
+
|
|
81
|
+
- **QUICK_REFERENCE.md** - Command examples
|
|
82
|
+
- **DESIGN.md** - Architecture details
|
|
83
|
+
- **SETUP_COMPLETE.md** - Full setup guide
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
MIT
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email account configuration management
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Load email accounts from environment variables
|
|
6
|
+
*/
|
|
7
|
+
export function loadAccounts() {
|
|
8
|
+
// Try JSON configuration first
|
|
9
|
+
const jsonConfig = process.env.EMAIL_ACCOUNTS_JSON;
|
|
10
|
+
if (jsonConfig) {
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(jsonConfig);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
throw new Error(`Failed to parse EMAIL_ACCOUNTS_JSON: ${error}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Fall back to single account from individual ENV vars
|
|
19
|
+
const singleAccount = {
|
|
20
|
+
smtp_host: process.env.SMTP_HOST || '',
|
|
21
|
+
smtp_port: parseInt(process.env.SMTP_PORT || '587'),
|
|
22
|
+
smtp_secure: process.env.SMTP_SECURE === 'true',
|
|
23
|
+
smtp_user: process.env.SMTP_USER || '',
|
|
24
|
+
smtp_pass: process.env.SMTP_PASS || '',
|
|
25
|
+
imap_host: process.env.IMAP_HOST || '',
|
|
26
|
+
imap_port: parseInt(process.env.IMAP_PORT || '993'),
|
|
27
|
+
imap_secure: process.env.IMAP_SECURE !== 'false', // Default to true
|
|
28
|
+
default_from_name: process.env.DEFAULT_FROM_NAME
|
|
29
|
+
};
|
|
30
|
+
// Validate single account has required fields
|
|
31
|
+
if (!singleAccount.smtp_host || !singleAccount.smtp_user || !singleAccount.smtp_pass) {
|
|
32
|
+
throw new Error('Email account configuration not found. Set EMAIL_ACCOUNTS_JSON or individual SMTP_* variables.');
|
|
33
|
+
}
|
|
34
|
+
// Use DEFAULT_EMAIL_ACCOUNT as the account name, or "default"
|
|
35
|
+
const accountName = process.env.DEFAULT_EMAIL_ACCOUNT || 'default';
|
|
36
|
+
return {
|
|
37
|
+
[accountName]: singleAccount
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get a specific email account by name
|
|
42
|
+
*/
|
|
43
|
+
export function getAccount(accountName) {
|
|
44
|
+
const accounts = loadAccounts();
|
|
45
|
+
// If no account name specified, use the default
|
|
46
|
+
if (!accountName) {
|
|
47
|
+
const defaultAccountName = process.env.DEFAULT_EMAIL_ACCOUNT || Object.keys(accounts)[0];
|
|
48
|
+
if (!defaultAccountName) {
|
|
49
|
+
throw new Error('No email accounts configured');
|
|
50
|
+
}
|
|
51
|
+
accountName = defaultAccountName;
|
|
52
|
+
}
|
|
53
|
+
const config = accounts[accountName];
|
|
54
|
+
if (!config) {
|
|
55
|
+
const availableAccounts = Object.keys(accounts).join(', ');
|
|
56
|
+
throw new Error(`Account "${accountName}" not found. Available accounts: ${availableAccounts}`);
|
|
57
|
+
}
|
|
58
|
+
return { name: accountName, config };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* List all configured accounts
|
|
62
|
+
*/
|
|
63
|
+
export function listAccounts() {
|
|
64
|
+
const accounts = loadAccounts();
|
|
65
|
+
return Object.keys(accounts);
|
|
66
|
+
}
|
package/build/config.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { logToFile } from "./index.js";
|
|
5
|
+
// Define paths for configuration and data storage
|
|
6
|
+
export const CONFIG_DIR = path.join(os.homedir(), '.smtp-mcp-server');
|
|
7
|
+
export const TEMPLATES_DIR = path.join(CONFIG_DIR, 'templates');
|
|
8
|
+
export const SMTP_CONFIG_FILE = path.join(CONFIG_DIR, 'smtp-config.json');
|
|
9
|
+
export const LOG_FILE = path.join(CONFIG_DIR, 'email-logs.json');
|
|
10
|
+
// Default SMTP configuration
|
|
11
|
+
export const DEFAULT_SMTP_CONFIG = {
|
|
12
|
+
smtpServers: [
|
|
13
|
+
{
|
|
14
|
+
id: 'example-smtp',
|
|
15
|
+
name: 'Example SMTP',
|
|
16
|
+
host: 'smtp.example.com',
|
|
17
|
+
port: 587,
|
|
18
|
+
secure: false,
|
|
19
|
+
auth: {
|
|
20
|
+
user: 'username',
|
|
21
|
+
pass: 'password'
|
|
22
|
+
},
|
|
23
|
+
isDefault: true
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
rateLimit: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
messagesPerMinute: 30
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
// Default email template
|
|
32
|
+
export const DEFAULT_TEMPLATE = {
|
|
33
|
+
id: 'default',
|
|
34
|
+
name: 'Default Template',
|
|
35
|
+
subject: 'Default Subject',
|
|
36
|
+
body: 'Hello {{name}},\n\nThis is a default email template.\n\nBest regards,\nThe Team',
|
|
37
|
+
isDefault: true
|
|
38
|
+
};
|
|
39
|
+
// Example business template
|
|
40
|
+
export const BUSINESS_TEMPLATE = {
|
|
41
|
+
id: 'business-outreach',
|
|
42
|
+
name: 'Business Outreach',
|
|
43
|
+
subject: 'Partnership Opportunity - {{company}}',
|
|
44
|
+
body: `Dear {{name}},
|
|
45
|
+
|
|
46
|
+
I hope this email finds you well. I'm reaching out to explore potential collaboration opportunities between our organizations.
|
|
47
|
+
|
|
48
|
+
We've been following {{company}}'s achievements and believe there could be synergies worth exploring.
|
|
49
|
+
|
|
50
|
+
Would you be available for a brief call to discuss this further? I'd love to learn more about your current initiatives.
|
|
51
|
+
|
|
52
|
+
Best regards,
|
|
53
|
+
{{sender_name}}
|
|
54
|
+
{{sender_email}}`,
|
|
55
|
+
isDefault: false
|
|
56
|
+
};
|
|
57
|
+
// Example newsletter template
|
|
58
|
+
export const NEWSLETTER_TEMPLATE = {
|
|
59
|
+
id: 'newsletter',
|
|
60
|
+
name: 'Monthly Newsletter',
|
|
61
|
+
subject: '{{month}} Newsletter - {{company}}',
|
|
62
|
+
body: `Dear {{name}},
|
|
63
|
+
|
|
64
|
+
Welcome to our {{month}} newsletter!
|
|
65
|
+
|
|
66
|
+
{{main_content}}
|
|
67
|
+
|
|
68
|
+
We hope you found this update valuable. If you have any questions, please don't hesitate to contact us.
|
|
69
|
+
|
|
70
|
+
Best regards,
|
|
71
|
+
The {{company}} Team
|
|
72
|
+
{{contact_email}}`,
|
|
73
|
+
isDefault: false
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Ensure all necessary directories and config files exist
|
|
77
|
+
*/
|
|
78
|
+
export async function ensureConfigDirectories() {
|
|
79
|
+
try {
|
|
80
|
+
// Create config directory if it doesn't exist
|
|
81
|
+
await fs.ensureDir(CONFIG_DIR);
|
|
82
|
+
await fs.ensureDir(TEMPLATES_DIR);
|
|
83
|
+
// Create default SMTP config if it doesn't exist
|
|
84
|
+
if (!await fs.pathExists(SMTP_CONFIG_FILE)) {
|
|
85
|
+
await fs.writeJson(SMTP_CONFIG_FILE, DEFAULT_SMTP_CONFIG, { spaces: 2 });
|
|
86
|
+
}
|
|
87
|
+
// Create default template if it doesn't exist
|
|
88
|
+
const defaultTemplatePath = path.join(TEMPLATES_DIR, 'default.json');
|
|
89
|
+
if (!await fs.pathExists(defaultTemplatePath)) {
|
|
90
|
+
await fs.writeJson(defaultTemplatePath, DEFAULT_TEMPLATE, { spaces: 2 });
|
|
91
|
+
}
|
|
92
|
+
// Create business template if it doesn't exist
|
|
93
|
+
const businessTemplatePath = path.join(TEMPLATES_DIR, 'business-outreach.json');
|
|
94
|
+
if (!await fs.pathExists(businessTemplatePath)) {
|
|
95
|
+
await fs.writeJson(businessTemplatePath, BUSINESS_TEMPLATE, { spaces: 2 });
|
|
96
|
+
}
|
|
97
|
+
// Create newsletter template if it doesn't exist
|
|
98
|
+
const newsletterTemplatePath = path.join(TEMPLATES_DIR, 'newsletter.json');
|
|
99
|
+
if (!await fs.pathExists(newsletterTemplatePath)) {
|
|
100
|
+
await fs.writeJson(newsletterTemplatePath, NEWSLETTER_TEMPLATE, { spaces: 2 });
|
|
101
|
+
}
|
|
102
|
+
// Create log file if it doesn't exist
|
|
103
|
+
if (!await fs.pathExists(LOG_FILE)) {
|
|
104
|
+
await fs.writeJson(LOG_FILE, [], { spaces: 2 });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
logToFile(`Error ensuring config directories: ${error}`);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get SMTP configurations
|
|
114
|
+
*/
|
|
115
|
+
export async function getSmtpConfigs() {
|
|
116
|
+
try {
|
|
117
|
+
const config = await fs.readJson(SMTP_CONFIG_FILE);
|
|
118
|
+
return config.smtpServers || [];
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
logToFile('Error reading SMTP config:');
|
|
122
|
+
return DEFAULT_SMTP_CONFIG.smtpServers;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get default SMTP configuration
|
|
127
|
+
*/
|
|
128
|
+
export async function getDefaultSmtpConfig() {
|
|
129
|
+
const configs = await getSmtpConfigs();
|
|
130
|
+
return configs.find(config => config.isDefault) || configs[0] || DEFAULT_SMTP_CONFIG.smtpServers[0];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Save SMTP configurations
|
|
134
|
+
*/
|
|
135
|
+
export async function saveSmtpConfigs(configs) {
|
|
136
|
+
try {
|
|
137
|
+
const currentConfig = await fs.readJson(SMTP_CONFIG_FILE);
|
|
138
|
+
currentConfig.smtpServers = configs;
|
|
139
|
+
await fs.writeJson(SMTP_CONFIG_FILE, currentConfig, { spaces: 2 });
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logToFile('Error saving SMTP config:');
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get email templates
|
|
149
|
+
*/
|
|
150
|
+
export async function getEmailTemplates() {
|
|
151
|
+
try {
|
|
152
|
+
const files = await fs.readdir(TEMPLATES_DIR);
|
|
153
|
+
const templates = [];
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
if (file.endsWith('.json')) {
|
|
156
|
+
const templatePath = path.join(TEMPLATES_DIR, file);
|
|
157
|
+
const template = await fs.readJson(templatePath);
|
|
158
|
+
templates.push(template);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return templates;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
logToFile('Error reading email templates:');
|
|
165
|
+
return [DEFAULT_TEMPLATE, BUSINESS_TEMPLATE, NEWSLETTER_TEMPLATE];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get default email template
|
|
170
|
+
*/
|
|
171
|
+
export async function getDefaultEmailTemplate() {
|
|
172
|
+
const templates = await getEmailTemplates();
|
|
173
|
+
return templates.find(template => template.isDefault) || templates[0] || DEFAULT_TEMPLATE;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Save email template
|
|
177
|
+
*/
|
|
178
|
+
export async function saveEmailTemplate(template) {
|
|
179
|
+
try {
|
|
180
|
+
const templatePath = path.join(TEMPLATES_DIR, `${template.id}.json`);
|
|
181
|
+
await fs.writeJson(templatePath, template, { spaces: 2 });
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
logToFile('Error saving email template:');
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Delete email template
|
|
191
|
+
*/
|
|
192
|
+
export async function deleteEmailTemplate(templateId) {
|
|
193
|
+
try {
|
|
194
|
+
const templatePath = path.join(TEMPLATES_DIR, `${templateId}.json`);
|
|
195
|
+
await fs.remove(templatePath);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
logToFile('Error deleting email template:');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Log email activity
|
|
205
|
+
*/
|
|
206
|
+
export async function logEmailActivity(entry) {
|
|
207
|
+
try {
|
|
208
|
+
let logs = [];
|
|
209
|
+
// Read existing logs if file exists
|
|
210
|
+
if (await fs.pathExists(LOG_FILE)) {
|
|
211
|
+
logs = await fs.readJson(LOG_FILE);
|
|
212
|
+
}
|
|
213
|
+
// Add new log entry
|
|
214
|
+
logs.push(entry);
|
|
215
|
+
// Write updated logs
|
|
216
|
+
await fs.writeJson(LOG_FILE, logs, { spaces: 2 });
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
logToFile('Error logging email activity:');
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get email logs
|
|
226
|
+
*/
|
|
227
|
+
export async function getEmailLogs() {
|
|
228
|
+
try {
|
|
229
|
+
if (await fs.pathExists(LOG_FILE)) {
|
|
230
|
+
return await fs.readJson(LOG_FILE);
|
|
231
|
+
}
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
logToFile('Error reading email logs:');
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool request handlers for email operations
|
|
3
|
+
*/
|
|
4
|
+
import { getAccount } from './accountManager.js';
|
|
5
|
+
import { searchEmails, modifyEmails, listFolders } from './imapService.js';
|
|
6
|
+
import { sendEmail, replyToEmail, forwardEmail } from './smtpService.js';
|
|
7
|
+
/**
|
|
8
|
+
* Handle emails_find tool
|
|
9
|
+
*/
|
|
10
|
+
export async function handleEmailsFind(args) {
|
|
11
|
+
try {
|
|
12
|
+
const { name, config } = getAccount(args.account_name);
|
|
13
|
+
const filters = args.filters || {};
|
|
14
|
+
const limit = args.limit || 20;
|
|
15
|
+
const includeContent = args.include_content || false;
|
|
16
|
+
const includeAttachments = args.include_attachments || false;
|
|
17
|
+
const emails = await searchEmails(config, filters, limit, includeContent, includeAttachments);
|
|
18
|
+
return JSON.stringify({
|
|
19
|
+
success: true,
|
|
20
|
+
account: name,
|
|
21
|
+
count: emails.length,
|
|
22
|
+
emails
|
|
23
|
+
}, null, 2);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return JSON.stringify({
|
|
27
|
+
success: false,
|
|
28
|
+
error: error.message || 'Failed to search emails'
|
|
29
|
+
}, null, 2);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Handle emails_modify tool
|
|
34
|
+
*/
|
|
35
|
+
export async function handleEmailsModify(args) {
|
|
36
|
+
try {
|
|
37
|
+
const { name, config } = getAccount(args.account_name);
|
|
38
|
+
if (!args.email_ids || args.email_ids.length === 0) {
|
|
39
|
+
throw new Error('email_ids is required and must not be empty');
|
|
40
|
+
}
|
|
41
|
+
const result = await modifyEmails(config, args.email_ids, {
|
|
42
|
+
markRead: args.mark_read,
|
|
43
|
+
markUnread: args.mark_unread,
|
|
44
|
+
flag: args.flag,
|
|
45
|
+
unflag: args.unflag,
|
|
46
|
+
moveToFolder: args.move_to_folder
|
|
47
|
+
});
|
|
48
|
+
return JSON.stringify({
|
|
49
|
+
success: result.success,
|
|
50
|
+
account: name,
|
|
51
|
+
modified: result.modified,
|
|
52
|
+
total: args.email_ids.length,
|
|
53
|
+
errors: result.errors
|
|
54
|
+
}, null, 2);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return JSON.stringify({
|
|
58
|
+
success: false,
|
|
59
|
+
error: error.message || 'Failed to modify emails'
|
|
60
|
+
}, null, 2);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Handle email_send tool
|
|
65
|
+
*/
|
|
66
|
+
export async function handleEmailSend(args) {
|
|
67
|
+
try {
|
|
68
|
+
const { name, config } = getAccount(args.account_name);
|
|
69
|
+
if (!args.to || args.to.length === 0) {
|
|
70
|
+
throw new Error('to is required and must not be empty');
|
|
71
|
+
}
|
|
72
|
+
if (!args.subject) {
|
|
73
|
+
throw new Error('subject is required');
|
|
74
|
+
}
|
|
75
|
+
if (!args.body) {
|
|
76
|
+
throw new Error('body is required');
|
|
77
|
+
}
|
|
78
|
+
const result = await sendEmail(config, {
|
|
79
|
+
to: args.to,
|
|
80
|
+
subject: args.subject,
|
|
81
|
+
body: args.body,
|
|
82
|
+
bodyType: args.body_type || 'html',
|
|
83
|
+
cc: args.cc,
|
|
84
|
+
bcc: args.bcc,
|
|
85
|
+
attachments: args.attachments,
|
|
86
|
+
fromName: config.default_from_name
|
|
87
|
+
});
|
|
88
|
+
return JSON.stringify({
|
|
89
|
+
success: result.success,
|
|
90
|
+
account: name,
|
|
91
|
+
message_id: result.messageId,
|
|
92
|
+
to: args.to,
|
|
93
|
+
subject: args.subject
|
|
94
|
+
}, null, 2);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return JSON.stringify({
|
|
98
|
+
success: false,
|
|
99
|
+
error: error.message || 'Failed to send email'
|
|
100
|
+
}, null, 2);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Handle email_respond tool
|
|
105
|
+
*/
|
|
106
|
+
export async function handleEmailRespond(args) {
|
|
107
|
+
try {
|
|
108
|
+
const { name, config } = getAccount(args.account_name);
|
|
109
|
+
if (!args.email_id) {
|
|
110
|
+
throw new Error('email_id is required');
|
|
111
|
+
}
|
|
112
|
+
if (!args.body) {
|
|
113
|
+
throw new Error('body is required');
|
|
114
|
+
}
|
|
115
|
+
const responseType = args.response_type || 'reply';
|
|
116
|
+
let result;
|
|
117
|
+
if (responseType === 'forward') {
|
|
118
|
+
if (!args.to || args.to.length === 0) {
|
|
119
|
+
throw new Error('to is required for forward');
|
|
120
|
+
}
|
|
121
|
+
result = await forwardEmail(config, args.email_id, {
|
|
122
|
+
to: args.to,
|
|
123
|
+
body: args.body,
|
|
124
|
+
bodyType: args.body_type || 'html',
|
|
125
|
+
includeOriginal: args.include_original !== false,
|
|
126
|
+
includeAttachments: args.include_attachments !== false,
|
|
127
|
+
additionalAttachments: args.additional_attachments
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
result = await replyToEmail(config, args.email_id, {
|
|
132
|
+
body: args.body,
|
|
133
|
+
bodyType: args.body_type || 'html',
|
|
134
|
+
replyAll: responseType === 'reply_all',
|
|
135
|
+
includeOriginal: args.include_original !== false,
|
|
136
|
+
includeAttachments: args.include_attachments !== false,
|
|
137
|
+
additionalAttachments: args.additional_attachments
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return JSON.stringify({
|
|
141
|
+
success: result.success,
|
|
142
|
+
account: name,
|
|
143
|
+
message_id: result.messageId,
|
|
144
|
+
response_type: responseType,
|
|
145
|
+
original_email_id: args.email_id
|
|
146
|
+
}, null, 2);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
return JSON.stringify({
|
|
150
|
+
success: false,
|
|
151
|
+
error: error.message || 'Failed to respond to email'
|
|
152
|
+
}, null, 2);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Handle folders_list tool
|
|
157
|
+
*/
|
|
158
|
+
export async function handleFoldersList(args) {
|
|
159
|
+
try {
|
|
160
|
+
const { name, config } = getAccount(args.account_name);
|
|
161
|
+
const folders = await listFolders(config, args.include_counts || false);
|
|
162
|
+
return JSON.stringify({
|
|
163
|
+
success: true,
|
|
164
|
+
account: name,
|
|
165
|
+
count: folders.length,
|
|
166
|
+
folders
|
|
167
|
+
}, null, 2);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return JSON.stringify({
|
|
171
|
+
success: false,
|
|
172
|
+
error: error.message || 'Failed to list folders'
|
|
173
|
+
}, null, 2);
|
|
174
|
+
}
|
|
175
|
+
}
|