@n24q02m/better-email-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/LICENSE +21 -0
- package/README.md +162 -0
- package/bin/cli.mjs +13 -0
- package/build/scripts/start-server.d.ts +6 -0
- package/build/scripts/start-server.d.ts.map +1 -0
- package/build/scripts/start-server.js +21 -0
- package/build/scripts/start-server.js.map +1 -0
- package/build/src/docs/attachments.md +48 -0
- package/build/src/docs/folders.md +45 -0
- package/build/src/docs/messages.md +92 -0
- package/build/src/docs/send.md +52 -0
- package/build/src/init-server.d.ts +40 -0
- package/build/src/init-server.d.ts.map +1 -0
- package/build/src/init-server.js +57 -0
- package/build/src/init-server.js.map +1 -0
- package/build/src/tools/composite/attachments.d.ts +17 -0
- package/build/src/tools/composite/attachments.d.ts.map +1 -0
- package/build/src/tools/composite/attachments.js +77 -0
- package/build/src/tools/composite/attachments.js.map +1 -0
- package/build/src/tools/composite/attachments.test.d.ts +2 -0
- package/build/src/tools/composite/attachments.test.d.ts.map +1 -0
- package/build/src/tools/composite/attachments.test.js +138 -0
- package/build/src/tools/composite/attachments.test.js.map +1 -0
- package/build/src/tools/composite/folders.d.ts +14 -0
- package/build/src/tools/composite/folders.d.ts.map +1 -0
- package/build/src/tools/composite/folders.js +57 -0
- package/build/src/tools/composite/folders.js.map +1 -0
- package/build/src/tools/composite/folders.test.d.ts +2 -0
- package/build/src/tools/composite/folders.test.d.ts.map +1 -0
- package/build/src/tools/composite/folders.test.js +61 -0
- package/build/src/tools/composite/folders.test.js.map +1 -0
- package/build/src/tools/composite/messages.d.ts +20 -0
- package/build/src/tools/composite/messages.d.ts.map +1 -0
- package/build/src/tools/composite/messages.js +244 -0
- package/build/src/tools/composite/messages.js.map +1 -0
- package/build/src/tools/composite/messages.test.d.ts +2 -0
- package/build/src/tools/composite/messages.test.d.ts.map +1 -0
- package/build/src/tools/composite/messages.test.js +217 -0
- package/build/src/tools/composite/messages.test.js.map +1 -0
- package/build/src/tools/composite/send.d.ts +21 -0
- package/build/src/tools/composite/send.d.ts.map +1 -0
- package/build/src/tools/composite/send.js +127 -0
- package/build/src/tools/composite/send.js.map +1 -0
- package/build/src/tools/composite/send.test.d.ts +2 -0
- package/build/src/tools/composite/send.test.d.ts.map +1 -0
- package/build/src/tools/composite/send.test.js +232 -0
- package/build/src/tools/composite/send.test.js.map +1 -0
- package/build/src/tools/helpers/config.d.ts +33 -0
- package/build/src/tools/helpers/config.d.ts.map +1 -0
- package/build/src/tools/helpers/config.js +167 -0
- package/build/src/tools/helpers/config.js.map +1 -0
- package/build/src/tools/helpers/config.test.d.ts +2 -0
- package/build/src/tools/helpers/config.test.d.ts.map +1 -0
- package/build/src/tools/helpers/config.test.js +141 -0
- package/build/src/tools/helpers/config.test.js.map +1 -0
- package/build/src/tools/helpers/errors.d.ts +34 -0
- package/build/src/tools/helpers/errors.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.js +152 -0
- package/build/src/tools/helpers/errors.js.map +1 -0
- package/build/src/tools/helpers/errors.test.d.ts +2 -0
- package/build/src/tools/helpers/errors.test.d.ts.map +1 -0
- package/build/src/tools/helpers/errors.test.js +203 -0
- package/build/src/tools/helpers/errors.test.js.map +1 -0
- package/build/src/tools/helpers/html-utils.d.ts +10 -0
- package/build/src/tools/helpers/html-utils.d.ts.map +1 -0
- package/build/src/tools/helpers/html-utils.js +29 -0
- package/build/src/tools/helpers/html-utils.js.map +1 -0
- package/build/src/tools/helpers/html-utils.test.d.ts +2 -0
- package/build/src/tools/helpers/html-utils.test.d.ts.map +1 -0
- package/build/src/tools/helpers/html-utils.test.js +103 -0
- package/build/src/tools/helpers/html-utils.test.js.map +1 -0
- package/build/src/tools/helpers/imap-client.d.ts +90 -0
- package/build/src/tools/helpers/imap-client.d.ts.map +1 -0
- package/build/src/tools/helpers/imap-client.js +321 -0
- package/build/src/tools/helpers/imap-client.js.map +1 -0
- package/build/src/tools/helpers/imap-client.test.d.ts +2 -0
- package/build/src/tools/helpers/imap-client.test.d.ts.map +1 -0
- package/build/src/tools/helpers/imap-client.test.js +401 -0
- package/build/src/tools/helpers/imap-client.test.js.map +1 -0
- package/build/src/tools/helpers/smtp-client.d.ts +38 -0
- package/build/src/tools/helpers/smtp-client.d.ts.map +1 -0
- package/build/src/tools/helpers/smtp-client.js +124 -0
- package/build/src/tools/helpers/smtp-client.js.map +1 -0
- package/build/src/tools/helpers/smtp-client.test.d.ts +2 -0
- package/build/src/tools/helpers/smtp-client.test.d.ts.map +1 -0
- package/build/src/tools/helpers/smtp-client.test.js +210 -0
- package/build/src/tools/helpers/smtp-client.test.js.map +1 -0
- package/build/src/tools/registry.d.ts +11 -0
- package/build/src/tools/registry.d.ts.map +1 -0
- package/build/src/tools/registry.js +262 -0
- package/build/src/tools/registry.js.map +1 -0
- package/build/src/tools/registry.test.d.ts +2 -0
- package/build/src/tools/registry.test.d.ts.map +1 -0
- package/build/src/tools/registry.test.js +135 -0
- package/build/src/tools/registry.test.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* AI-friendly error messages and suggestions for email operations
|
|
4
|
+
*/
|
|
5
|
+
export declare class EmailMCPError extends Error {
|
|
6
|
+
code: string;
|
|
7
|
+
suggestion?: string | undefined;
|
|
8
|
+
details?: any | undefined;
|
|
9
|
+
constructor(message: string, code: string, suggestion?: string | undefined, details?: any | undefined);
|
|
10
|
+
toJSON(): {
|
|
11
|
+
error: string;
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
suggestion: string | undefined;
|
|
15
|
+
details: any;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Enhance email-related errors with helpful context
|
|
20
|
+
*/
|
|
21
|
+
export declare function enhanceError(error: any): EmailMCPError;
|
|
22
|
+
/**
|
|
23
|
+
* Create AI-readable error message
|
|
24
|
+
*/
|
|
25
|
+
export declare function aiReadableMessage(error: EmailMCPError): string;
|
|
26
|
+
/**
|
|
27
|
+
* Suggest fixes based on error
|
|
28
|
+
*/
|
|
29
|
+
export declare function suggestFixes(error: EmailMCPError): string[];
|
|
30
|
+
/**
|
|
31
|
+
* Wrap async function with error handling
|
|
32
|
+
*/
|
|
33
|
+
export declare function withErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T): (...args: Parameters<T>) => Promise<ReturnType<T>>;
|
|
34
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,aAAc,SAAQ,KAAK;IAG7B,IAAI,EAAE,MAAM;IACZ,UAAU,CAAC,EAAE,MAAM;IACnB,OAAO,CAAC,EAAE,GAAG;gBAHpB,OAAO,EAAE,MAAM,EACR,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,OAAO,CAAC,EAAE,GAAG,YAAA;IAMtB,MAAM;;;;;;;CASP;AAoBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,GAAG,GAAG,aAAa,CAgEtD;AAyCD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAY9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAqC3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAC1E,EAAE,EAAE,CAAC,GACJ,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAQpD"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Utilities
|
|
3
|
+
* AI-friendly error messages and suggestions for email operations
|
|
4
|
+
*/
|
|
5
|
+
export class EmailMCPError extends Error {
|
|
6
|
+
constructor(message, code, suggestion, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.suggestion = suggestion;
|
|
10
|
+
this.details = details;
|
|
11
|
+
this.name = 'EmailMCPError';
|
|
12
|
+
}
|
|
13
|
+
toJSON() {
|
|
14
|
+
return {
|
|
15
|
+
error: this.name,
|
|
16
|
+
code: this.code,
|
|
17
|
+
message: this.message,
|
|
18
|
+
suggestion: this.suggestion,
|
|
19
|
+
details: this.details
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize error object to remove sensitive information (passwords, tokens)
|
|
25
|
+
*/
|
|
26
|
+
function sanitizeErrorDetails(error) {
|
|
27
|
+
if (!error || typeof error !== 'object')
|
|
28
|
+
return error;
|
|
29
|
+
const safe = {
|
|
30
|
+
message: error.message,
|
|
31
|
+
name: error.name,
|
|
32
|
+
code: error.code
|
|
33
|
+
};
|
|
34
|
+
if (error.status)
|
|
35
|
+
safe.status = error.status;
|
|
36
|
+
if (error.responseCode)
|
|
37
|
+
safe.responseCode = error.responseCode;
|
|
38
|
+
return safe;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Enhance email-related errors with helpful context
|
|
42
|
+
*/
|
|
43
|
+
export function enhanceError(error) {
|
|
44
|
+
const message = error.message || 'Unknown error occurred';
|
|
45
|
+
// IMAP authentication errors
|
|
46
|
+
if (message.includes('Invalid credentials') ||
|
|
47
|
+
message.includes('AUTHENTICATIONFAILED') ||
|
|
48
|
+
error.authenticationFailed) {
|
|
49
|
+
return new EmailMCPError('Email authentication failed', 'AUTH_FAILED', 'Check that your email and App Password are correct. For Gmail: enable 2FA then create an App Password at https://myaccount.google.com/apppasswords. For Outlook: enable 2FA then create an App Password in security settings.');
|
|
50
|
+
}
|
|
51
|
+
// Connection errors
|
|
52
|
+
if (message.includes('ECONNREFUSED') || message.includes('ENOTFOUND') || message.includes('ETIMEDOUT')) {
|
|
53
|
+
return new EmailMCPError('Cannot connect to email server', 'CONNECTION_ERROR', 'Check your internet connection and verify the email server address is correct.');
|
|
54
|
+
}
|
|
55
|
+
// TLS/SSL errors
|
|
56
|
+
if (message.includes('CERT') || message.includes('SSL') || message.includes('TLS')) {
|
|
57
|
+
return new EmailMCPError('TLS/SSL connection error', 'TLS_ERROR', 'The email server certificate could not be verified. Check the server address and port.');
|
|
58
|
+
}
|
|
59
|
+
// IMAP mailbox errors
|
|
60
|
+
if (message.includes('Mailbox not found') || message.includes('NO [NONEXISTENT]')) {
|
|
61
|
+
return new EmailMCPError('Mailbox/folder not found', 'FOLDER_NOT_FOUND', 'Check the folder name. Use the folders tool to list available folders.');
|
|
62
|
+
}
|
|
63
|
+
// SMTP errors
|
|
64
|
+
if (error.responseCode) {
|
|
65
|
+
return handleSmtpError(error);
|
|
66
|
+
}
|
|
67
|
+
// Configuration errors
|
|
68
|
+
if (message.includes('EMAIL_CREDENTIALS')) {
|
|
69
|
+
return new EmailMCPError('EMAIL_CREDENTIALS environment variable is required', 'CONFIG_ERROR', 'Set EMAIL_CREDENTIALS in format: email1:password1,email2:password2');
|
|
70
|
+
}
|
|
71
|
+
// Generic error
|
|
72
|
+
return new EmailMCPError(message, 'UNKNOWN_ERROR', 'Please check your request and try again', sanitizeErrorDetails(error));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Handle SMTP-specific errors
|
|
76
|
+
*/
|
|
77
|
+
function handleSmtpError(error) {
|
|
78
|
+
const code = error.responseCode;
|
|
79
|
+
switch (code) {
|
|
80
|
+
case 535:
|
|
81
|
+
return new EmailMCPError('SMTP authentication failed', 'SMTP_AUTH_FAILED', 'Check your email and App Password for the sending account.');
|
|
82
|
+
case 550:
|
|
83
|
+
return new EmailMCPError('Recipient address rejected', 'RECIPIENT_REJECTED', 'Check the recipient email address is correct and exists.');
|
|
84
|
+
case 552:
|
|
85
|
+
case 554:
|
|
86
|
+
return new EmailMCPError('Message rejected by server', 'MESSAGE_REJECTED', 'The email was rejected. It may be too large or flagged as spam.');
|
|
87
|
+
default:
|
|
88
|
+
return new EmailMCPError(error.message || `SMTP error ${code}`, `SMTP_${code}`, 'Check the SMTP error code and try again.', sanitizeErrorDetails(error));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create AI-readable error message
|
|
93
|
+
*/
|
|
94
|
+
export function aiReadableMessage(error) {
|
|
95
|
+
let message = `Error: ${error.message}`;
|
|
96
|
+
if (error.suggestion) {
|
|
97
|
+
message += `\n\nSuggestion: ${error.suggestion}`;
|
|
98
|
+
}
|
|
99
|
+
if (error.details) {
|
|
100
|
+
message += `\n\nDetails: ${JSON.stringify(error.details, null, 2)}`;
|
|
101
|
+
}
|
|
102
|
+
return message;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Suggest fixes based on error
|
|
106
|
+
*/
|
|
107
|
+
export function suggestFixes(error) {
|
|
108
|
+
const suggestions = [];
|
|
109
|
+
switch (error.code) {
|
|
110
|
+
case 'AUTH_FAILED':
|
|
111
|
+
case 'SMTP_AUTH_FAILED':
|
|
112
|
+
suggestions.push('Verify your App Password is correct (not your regular password)');
|
|
113
|
+
suggestions.push('For Gmail: https://myaccount.google.com/apppasswords');
|
|
114
|
+
suggestions.push('For Outlook: Security settings → App passwords');
|
|
115
|
+
suggestions.push('Ensure 2-Factor Authentication is enabled on your account');
|
|
116
|
+
break;
|
|
117
|
+
case 'CONNECTION_ERROR':
|
|
118
|
+
suggestions.push('Check your internet connection');
|
|
119
|
+
suggestions.push('Verify the email server address');
|
|
120
|
+
suggestions.push('Check if a firewall is blocking the connection');
|
|
121
|
+
break;
|
|
122
|
+
case 'FOLDER_NOT_FOUND':
|
|
123
|
+
suggestions.push('Use the folders tool to list available folders');
|
|
124
|
+
suggestions.push('Folder names are case-sensitive');
|
|
125
|
+
suggestions.push('Gmail uses labels (e.g. [Gmail]/All Mail) instead of traditional folders');
|
|
126
|
+
break;
|
|
127
|
+
case 'CONFIG_ERROR':
|
|
128
|
+
suggestions.push('Set EMAIL_CREDENTIALS environment variable');
|
|
129
|
+
suggestions.push('Format: email1:password1,email2:password2');
|
|
130
|
+
suggestions.push('Passwords with colons can use format: email:password:imap_host');
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
suggestions.push('Check the error message for details');
|
|
134
|
+
suggestions.push('Try again in a few moments');
|
|
135
|
+
suggestions.push('Verify your email account settings');
|
|
136
|
+
}
|
|
137
|
+
return suggestions;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Wrap async function with error handling
|
|
141
|
+
*/
|
|
142
|
+
export function withErrorHandling(fn) {
|
|
143
|
+
return async (...args) => {
|
|
144
|
+
try {
|
|
145
|
+
return await fn(...args);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw enhanceError(error);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { aiReadableMessage, EmailMCPError, enhanceError, suggestFixes, withErrorHandling } from './errors.js';
|
|
3
|
+
describe('EmailMCPError', () => {
|
|
4
|
+
it('creates error with message and code', () => {
|
|
5
|
+
const error = new EmailMCPError('test error', 'TEST_CODE');
|
|
6
|
+
expect(error.message).toBe('test error');
|
|
7
|
+
expect(error.code).toBe('TEST_CODE');
|
|
8
|
+
expect(error.name).toBe('EmailMCPError');
|
|
9
|
+
expect(error.suggestion).toBeUndefined();
|
|
10
|
+
expect(error.details).toBeUndefined();
|
|
11
|
+
});
|
|
12
|
+
it('creates error with suggestion and details', () => {
|
|
13
|
+
const error = new EmailMCPError('test', 'CODE', 'try this', { key: 'value' });
|
|
14
|
+
expect(error.suggestion).toBe('try this');
|
|
15
|
+
expect(error.details).toEqual({ key: 'value' });
|
|
16
|
+
});
|
|
17
|
+
it('serializes to JSON correctly', () => {
|
|
18
|
+
const error = new EmailMCPError('msg', 'CODE', 'hint', { x: 1 });
|
|
19
|
+
const json = error.toJSON();
|
|
20
|
+
expect(json).toEqual({
|
|
21
|
+
error: 'EmailMCPError',
|
|
22
|
+
code: 'CODE',
|
|
23
|
+
message: 'msg',
|
|
24
|
+
suggestion: 'hint',
|
|
25
|
+
details: { x: 1 }
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it('is an instance of Error', () => {
|
|
29
|
+
const error = new EmailMCPError('test', 'CODE');
|
|
30
|
+
expect(error).toBeInstanceOf(Error);
|
|
31
|
+
expect(error).toBeInstanceOf(EmailMCPError);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('enhanceError', () => {
|
|
35
|
+
it('handles IMAP auth errors', () => {
|
|
36
|
+
const result = enhanceError({ message: 'AUTHENTICATIONFAILED' });
|
|
37
|
+
expect(result.code).toBe('AUTH_FAILED');
|
|
38
|
+
expect(result.suggestion).toContain('App Password');
|
|
39
|
+
});
|
|
40
|
+
it('handles Invalid credentials', () => {
|
|
41
|
+
const result = enhanceError({ message: 'Invalid credentials' });
|
|
42
|
+
expect(result.code).toBe('AUTH_FAILED');
|
|
43
|
+
});
|
|
44
|
+
it('handles authenticationFailed flag', () => {
|
|
45
|
+
const result = enhanceError({ message: 'some error', authenticationFailed: true });
|
|
46
|
+
expect(result.code).toBe('AUTH_FAILED');
|
|
47
|
+
});
|
|
48
|
+
it('handles connection refused', () => {
|
|
49
|
+
const result = enhanceError({ message: 'ECONNREFUSED' });
|
|
50
|
+
expect(result.code).toBe('CONNECTION_ERROR');
|
|
51
|
+
});
|
|
52
|
+
it('handles DNS not found', () => {
|
|
53
|
+
const result = enhanceError({ message: 'ENOTFOUND' });
|
|
54
|
+
expect(result.code).toBe('CONNECTION_ERROR');
|
|
55
|
+
});
|
|
56
|
+
it('handles timeout', () => {
|
|
57
|
+
const result = enhanceError({ message: 'ETIMEDOUT' });
|
|
58
|
+
expect(result.code).toBe('CONNECTION_ERROR');
|
|
59
|
+
});
|
|
60
|
+
it('handles TLS errors', () => {
|
|
61
|
+
const result = enhanceError({ message: 'SSL handshake failed' });
|
|
62
|
+
expect(result.code).toBe('TLS_ERROR');
|
|
63
|
+
});
|
|
64
|
+
it('handles CERT errors', () => {
|
|
65
|
+
const result = enhanceError({ message: 'CERT_HAS_EXPIRED' });
|
|
66
|
+
expect(result.code).toBe('TLS_ERROR');
|
|
67
|
+
});
|
|
68
|
+
it('handles mailbox not found', () => {
|
|
69
|
+
const result = enhanceError({ message: 'Mailbox not found' });
|
|
70
|
+
expect(result.code).toBe('FOLDER_NOT_FOUND');
|
|
71
|
+
});
|
|
72
|
+
it('handles IMAP NONEXISTENT', () => {
|
|
73
|
+
const result = enhanceError({ message: 'NO [NONEXISTENT] folder' });
|
|
74
|
+
expect(result.code).toBe('FOLDER_NOT_FOUND');
|
|
75
|
+
});
|
|
76
|
+
it('handles SMTP 535 auth error', () => {
|
|
77
|
+
const result = enhanceError({ message: 'auth failed', responseCode: 535 });
|
|
78
|
+
expect(result.code).toBe('SMTP_AUTH_FAILED');
|
|
79
|
+
});
|
|
80
|
+
it('handles SMTP 550 recipient rejected', () => {
|
|
81
|
+
const result = enhanceError({ message: 'rejected', responseCode: 550 });
|
|
82
|
+
expect(result.code).toBe('RECIPIENT_REJECTED');
|
|
83
|
+
});
|
|
84
|
+
it('handles SMTP 552 message rejected', () => {
|
|
85
|
+
const result = enhanceError({ message: 'too large', responseCode: 552 });
|
|
86
|
+
expect(result.code).toBe('MESSAGE_REJECTED');
|
|
87
|
+
});
|
|
88
|
+
it('handles SMTP 554 message rejected', () => {
|
|
89
|
+
const result = enhanceError({ message: 'spam', responseCode: 554 });
|
|
90
|
+
expect(result.code).toBe('MESSAGE_REJECTED');
|
|
91
|
+
});
|
|
92
|
+
it('handles unknown SMTP error codes', () => {
|
|
93
|
+
const result = enhanceError({ message: 'unknown', responseCode: 421 });
|
|
94
|
+
expect(result.code).toBe('SMTP_421');
|
|
95
|
+
});
|
|
96
|
+
it('handles EMAIL_CREDENTIALS config error', () => {
|
|
97
|
+
const result = enhanceError({ message: 'EMAIL_CREDENTIALS is missing' });
|
|
98
|
+
expect(result.code).toBe('CONFIG_ERROR');
|
|
99
|
+
});
|
|
100
|
+
it('handles generic unknown errors', () => {
|
|
101
|
+
const result = enhanceError({ message: 'something weird happened' });
|
|
102
|
+
expect(result.code).toBe('UNKNOWN_ERROR');
|
|
103
|
+
});
|
|
104
|
+
it('handles errors with no message', () => {
|
|
105
|
+
const result = enhanceError({});
|
|
106
|
+
expect(result.code).toBe('UNKNOWN_ERROR');
|
|
107
|
+
expect(result.message).toBe('Unknown error occurred');
|
|
108
|
+
});
|
|
109
|
+
it('sanitizes error details (no password leakage)', () => {
|
|
110
|
+
const result = enhanceError({ message: 'generic', password: 'secret123', status: 500 });
|
|
111
|
+
// details should not contain password
|
|
112
|
+
if (result.details) {
|
|
113
|
+
expect(result.details.password).toBeUndefined();
|
|
114
|
+
expect(result.details.status).toBe(500);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('aiReadableMessage', () => {
|
|
119
|
+
it('formats error without suggestion or details', () => {
|
|
120
|
+
const error = new EmailMCPError('test error', 'CODE');
|
|
121
|
+
const msg = aiReadableMessage(error);
|
|
122
|
+
expect(msg).toBe('Error: test error');
|
|
123
|
+
});
|
|
124
|
+
it('includes suggestion when present', () => {
|
|
125
|
+
const error = new EmailMCPError('test', 'CODE', 'try this');
|
|
126
|
+
const msg = aiReadableMessage(error);
|
|
127
|
+
expect(msg).toContain('Suggestion: try this');
|
|
128
|
+
});
|
|
129
|
+
it('includes details when present', () => {
|
|
130
|
+
const error = new EmailMCPError('test', 'CODE', undefined, { key: 'val' });
|
|
131
|
+
const msg = aiReadableMessage(error);
|
|
132
|
+
expect(msg).toContain('Details:');
|
|
133
|
+
expect(msg).toContain('"key": "val"');
|
|
134
|
+
});
|
|
135
|
+
it('includes both suggestion and details', () => {
|
|
136
|
+
const error = new EmailMCPError('test', 'CODE', 'hint', { x: 1 });
|
|
137
|
+
const msg = aiReadableMessage(error);
|
|
138
|
+
expect(msg).toContain('Error: test');
|
|
139
|
+
expect(msg).toContain('Suggestion: hint');
|
|
140
|
+
expect(msg).toContain('Details:');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe('suggestFixes', () => {
|
|
144
|
+
it('returns auth suggestions for AUTH_FAILED', () => {
|
|
145
|
+
const error = new EmailMCPError('auth', 'AUTH_FAILED');
|
|
146
|
+
const fixes = suggestFixes(error);
|
|
147
|
+
expect(fixes.length).toBeGreaterThan(0);
|
|
148
|
+
expect(fixes.some((f) => f.includes('App Password'))).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
it('returns auth suggestions for SMTP_AUTH_FAILED', () => {
|
|
151
|
+
const error = new EmailMCPError('smtp auth', 'SMTP_AUTH_FAILED');
|
|
152
|
+
const fixes = suggestFixes(error);
|
|
153
|
+
expect(fixes.some((f) => f.includes('App Password'))).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('returns connection suggestions for CONNECTION_ERROR', () => {
|
|
156
|
+
const error = new EmailMCPError('conn', 'CONNECTION_ERROR');
|
|
157
|
+
const fixes = suggestFixes(error);
|
|
158
|
+
expect(fixes.some((f) => f.includes('internet'))).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
it('returns folder suggestions for FOLDER_NOT_FOUND', () => {
|
|
161
|
+
const error = new EmailMCPError('folder', 'FOLDER_NOT_FOUND');
|
|
162
|
+
const fixes = suggestFixes(error);
|
|
163
|
+
expect(fixes.some((f) => f.includes('folders tool'))).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
it('returns config suggestions for CONFIG_ERROR', () => {
|
|
166
|
+
const error = new EmailMCPError('config', 'CONFIG_ERROR');
|
|
167
|
+
const fixes = suggestFixes(error);
|
|
168
|
+
expect(fixes.some((f) => f.includes('EMAIL_CREDENTIALS'))).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
it('returns generic suggestions for unknown codes', () => {
|
|
171
|
+
const error = new EmailMCPError('unknown', 'RANDOM_CODE');
|
|
172
|
+
const fixes = suggestFixes(error);
|
|
173
|
+
expect(fixes.length).toBeGreaterThan(0);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe('withErrorHandling', () => {
|
|
177
|
+
it('passes through successful results', async () => {
|
|
178
|
+
const fn = async () => 'success';
|
|
179
|
+
const wrapped = withErrorHandling(fn);
|
|
180
|
+
const result = await wrapped();
|
|
181
|
+
expect(result).toBe('success');
|
|
182
|
+
});
|
|
183
|
+
it('enhances thrown errors', async () => {
|
|
184
|
+
const fn = async () => {
|
|
185
|
+
throw new Error('AUTHENTICATIONFAILED');
|
|
186
|
+
};
|
|
187
|
+
const wrapped = withErrorHandling(fn);
|
|
188
|
+
await expect(wrapped()).rejects.toThrow(EmailMCPError);
|
|
189
|
+
try {
|
|
190
|
+
await wrapped();
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
|
+
expect(e.code).toBe('AUTH_FAILED');
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
it('passes arguments through', async () => {
|
|
197
|
+
const fn = async (a, b) => a + b;
|
|
198
|
+
const wrapped = withErrorHandling(fn);
|
|
199
|
+
const result = await wrapped(2, 3);
|
|
200
|
+
expect(result).toBe(5);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
//# sourceMappingURL=errors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.test.js","sourceRoot":"","sources":["../../../../src/tools/helpers/errors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML to Clean Text Utilities
|
|
3
|
+
* Strips HTML tags, CSS, scripts and returns clean text for LLM consumption
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Convert HTML email body to clean plain text
|
|
7
|
+
* Removes CSS, scripts, images, and formatting noise to save LLM tokens
|
|
8
|
+
*/
|
|
9
|
+
export declare function htmlToCleanText(html: string): string;
|
|
10
|
+
//# sourceMappingURL=html-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/html-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBpD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML to Clean Text Utilities
|
|
3
|
+
* Strips HTML tags, CSS, scripts and returns clean text for LLM consumption
|
|
4
|
+
*/
|
|
5
|
+
import { convert } from 'html-to-text';
|
|
6
|
+
/**
|
|
7
|
+
* Convert HTML email body to clean plain text
|
|
8
|
+
* Removes CSS, scripts, images, and formatting noise to save LLM tokens
|
|
9
|
+
*/
|
|
10
|
+
export function htmlToCleanText(html) {
|
|
11
|
+
if (!html)
|
|
12
|
+
return '';
|
|
13
|
+
return convert(html, {
|
|
14
|
+
wordwrap: false,
|
|
15
|
+
preserveNewlines: true,
|
|
16
|
+
selectors: [
|
|
17
|
+
// Remove style and script tags entirely
|
|
18
|
+
{ selector: 'style', format: 'skip' },
|
|
19
|
+
{ selector: 'script', format: 'skip' },
|
|
20
|
+
// Remove images (just show alt text if any)
|
|
21
|
+
{ selector: 'img', format: 'skip' },
|
|
22
|
+
// Keep links as text
|
|
23
|
+
{ selector: 'a', options: { hideLinkHrefIfSameAsText: true, ignoreHref: false } },
|
|
24
|
+
// Tables as readable text
|
|
25
|
+
{ selector: 'table', format: 'dataTable' }
|
|
26
|
+
]
|
|
27
|
+
}).trim();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=html-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.js","sourceRoot":"","sources":["../../../../src/tools/helpers/html-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAA;IAEpB,OAAO,OAAO,CAAC,IAAI,EAAE;QACnB,QAAQ,EAAE,KAAK;QACf,gBAAgB,EAAE,IAAI;QACtB,SAAS,EAAE;YACT,wCAAwC;YACxC,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;YACrC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;YACtC,4CAA4C;YAC5C,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;YACnC,qBAAqB;YACrB,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,wBAAwB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE;YACjF,0BAA0B;YAC1B,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE;SAC3C;KACF,CAAC,CAAC,IAAI,EAAE,CAAA;AACX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.test.d.ts","sourceRoot":"","sources":["../../../../src/tools/helpers/html-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { htmlToCleanText } from './html-utils.js';
|
|
3
|
+
describe('htmlToCleanText', () => {
|
|
4
|
+
it('returns empty string for empty input', () => {
|
|
5
|
+
expect(htmlToCleanText('')).toBe('');
|
|
6
|
+
});
|
|
7
|
+
it('returns empty string for null-like input', () => {
|
|
8
|
+
expect(htmlToCleanText(null)).toBe('');
|
|
9
|
+
expect(htmlToCleanText(undefined)).toBe('');
|
|
10
|
+
});
|
|
11
|
+
it('strips HTML tags', () => {
|
|
12
|
+
const result = htmlToCleanText('<p>Hello <b>World</b></p>');
|
|
13
|
+
expect(result).toContain('Hello');
|
|
14
|
+
expect(result).toContain('World');
|
|
15
|
+
expect(result).not.toContain('<p>');
|
|
16
|
+
expect(result).not.toContain('<b>');
|
|
17
|
+
});
|
|
18
|
+
it('removes style tags', () => {
|
|
19
|
+
const result = htmlToCleanText('<style>.red { color: red; }</style><p>Content</p>');
|
|
20
|
+
expect(result).not.toContain('color');
|
|
21
|
+
expect(result).toContain('Content');
|
|
22
|
+
});
|
|
23
|
+
it('removes script tags', () => {
|
|
24
|
+
const result = htmlToCleanText('<script>alert("xss")</script><p>Safe</p>');
|
|
25
|
+
expect(result).not.toContain('alert');
|
|
26
|
+
expect(result).not.toContain('xss');
|
|
27
|
+
expect(result).toContain('Safe');
|
|
28
|
+
});
|
|
29
|
+
it('removes img tags', () => {
|
|
30
|
+
const result = htmlToCleanText('<p>Before</p><img src="test.png" alt="test"><p>After</p>');
|
|
31
|
+
expect(result).not.toContain('test.png');
|
|
32
|
+
expect(result).toContain('Before');
|
|
33
|
+
expect(result).toContain('After');
|
|
34
|
+
});
|
|
35
|
+
it('preserves link text', () => {
|
|
36
|
+
const result = htmlToCleanText('<a href="https://example.com">Click here</a>');
|
|
37
|
+
expect(result).toContain('Click here');
|
|
38
|
+
});
|
|
39
|
+
it('handles links with href', () => {
|
|
40
|
+
const result = htmlToCleanText('<a href="https://example.com">Visit</a>');
|
|
41
|
+
expect(result).toContain('Visit');
|
|
42
|
+
expect(result).toContain('https://example.com');
|
|
43
|
+
});
|
|
44
|
+
it('hides href when same as text', () => {
|
|
45
|
+
const result = htmlToCleanText('<a href="https://example.com">https://example.com</a>');
|
|
46
|
+
// Should not duplicate the URL
|
|
47
|
+
const count = (result.match(/https:\/\/example\.com/g) || []).length;
|
|
48
|
+
expect(count).toBe(1);
|
|
49
|
+
});
|
|
50
|
+
it('handles nested HTML', () => {
|
|
51
|
+
const html = '<div><h1>Title</h1><p>Paragraph with <strong>bold</strong> text.</p></div>';
|
|
52
|
+
const result = htmlToCleanText(html);
|
|
53
|
+
// html-to-text uppercases headings
|
|
54
|
+
expect(result.toUpperCase()).toContain('TITLE');
|
|
55
|
+
expect(result).toContain('bold');
|
|
56
|
+
expect(result).toContain('text');
|
|
57
|
+
});
|
|
58
|
+
it('preserves newlines', () => {
|
|
59
|
+
const result = htmlToCleanText('<p>Line 1</p><p>Line 2</p>');
|
|
60
|
+
expect(result).toContain('Line 1');
|
|
61
|
+
expect(result).toContain('Line 2');
|
|
62
|
+
});
|
|
63
|
+
it('handles tables', () => {
|
|
64
|
+
const html = '<table><tr><th>Name</th><th>Value</th></tr><tr><td>A</td><td>1</td></tr></table>';
|
|
65
|
+
const result = htmlToCleanText(html);
|
|
66
|
+
// html-to-text uppercases table headers
|
|
67
|
+
expect(result.toUpperCase()).toContain('NAME');
|
|
68
|
+
expect(result.toUpperCase()).toContain('VALUE');
|
|
69
|
+
expect(result).toContain('A');
|
|
70
|
+
expect(result).toContain('1');
|
|
71
|
+
});
|
|
72
|
+
it('trims output', () => {
|
|
73
|
+
const result = htmlToCleanText(' <p> Content </p> ');
|
|
74
|
+
expect(result).not.toMatch(/^\s/);
|
|
75
|
+
expect(result).not.toMatch(/\s$/);
|
|
76
|
+
});
|
|
77
|
+
it('handles complex email HTML', () => {
|
|
78
|
+
const html = `
|
|
79
|
+
<html>
|
|
80
|
+
<head><style>body { font-family: Arial; }</style></head>
|
|
81
|
+
<body>
|
|
82
|
+
<div style="max-width: 600px;">
|
|
83
|
+
<h1>Newsletter</h1>
|
|
84
|
+
<p>Hello <strong>User</strong>,</p>
|
|
85
|
+
<p>Check out our <a href="https://example.com/deals">latest deals</a>.</p>
|
|
86
|
+
<img src="banner.jpg" alt="Banner">
|
|
87
|
+
<script>tracking()</script>
|
|
88
|
+
</div>
|
|
89
|
+
</body>
|
|
90
|
+
</html>
|
|
91
|
+
`;
|
|
92
|
+
const result = htmlToCleanText(html);
|
|
93
|
+
// html-to-text uppercases headings
|
|
94
|
+
expect(result.toUpperCase()).toContain('NEWSLETTER');
|
|
95
|
+
expect(result).toContain('Hello');
|
|
96
|
+
expect(result).toContain('User');
|
|
97
|
+
expect(result).toContain('latest deals');
|
|
98
|
+
expect(result).not.toContain('font-family');
|
|
99
|
+
expect(result).not.toContain('tracking');
|
|
100
|
+
expect(result).not.toContain('banner.jpg');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=html-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.test.js","sourceRoot":"","sources":["../../../../src/tools/helpers/html-utils.test.ts"],"names":[],"mappings":"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"}
|