Skip to main content
Glama
email-validation.ts3.92 kB
/** * Email validation utilities for the Attio MCP Server * * Provides RFC 5322 compliant email validation that is consistent * across the application. This replaces the inconsistent simple regex * patterns that were scattered throughout the codebase. */ /** * Validates an email address according to RFC 5322 with practical limitations * * This validation is more comprehensive than the previous simple regex and handles: * - International domains * - Plus addressing (user+tag@domain.com) * - Multiple subdomains * - TLDs from 2 to 63 characters * - Proper length limits per RFC 5321 * * @param email - The email address to validate * @returns true if the email is valid, false otherwise */ export function isValidEmail(email: string): boolean { if (!email || typeof email !== 'string') { return false; } // Trim whitespace email = email.trim(); // Check for reasonable length limits (RFC 5321) if (email.length === 0 || email.length > 254) { return false; } // More comprehensive email validation regex based on RFC 5322 // This handles international domains, plus addressing, multiple subdomains, etc. const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; if (!emailRegex.test(email)) { return false; } // Additional validation for edge cases const parts = email.split('@'); if (parts.length !== 2) { return false; } const [localPart, domain] = parts; // Check local part length (before @) - RFC 5321 limit if (localPart.length === 0 || localPart.length > 64) { return false; } // Check for consecutive dots in local part (not allowed per RFC 5322) if (localPart.includes('..')) { return false; } // Local part cannot start or end with dot if (localPart.startsWith('.') || localPart.endsWith('.')) { return false; } // Check domain has at least one dot if (!domain.includes('.')) { return false; } // Check domain parts const domainParts = domain.split('.'); for (const part of domainParts) { // Each domain part should not be empty and should not exceed 63 characters if (part.length === 0 || part.length > 63) { return false; } // Domain parts should not start or end with hyphen if (part.startsWith('-') || part.endsWith('-')) { return false; } } return true; } /** * Validates an array of email addresses * * @param emails - Array of email addresses to validate * @returns An object with valid and invalid email arrays */ export function validateEmails(emails: string[]): { valid: string[]; invalid: string[]; } { const valid: string[] = []; const invalid: string[] = []; for (const email of emails) { if (isValidEmail(email)) { valid.push(email); } else { invalid.push(email); } } return { valid, invalid }; } /** * Normalizes an email address to lowercase * Note: Only the domain part should be normalized to lowercase per RFC. * The local part (before @) is case-sensitive per spec, but most * email providers treat it as case-insensitive. * * @param email - Email address to normalize * @returns Normalized email address or null if invalid */ export function normalizeEmail(email: string): string | null { if (!isValidEmail(email)) { return null; } // For practical purposes, convert entire email to lowercase // as most email providers treat the local part as case-insensitive return email.trim().toLowerCase(); } /** * Extracts the domain from an email address * * @param email - Email address to extract domain from * @returns Domain part of the email or null if invalid */ export function extractEmailDomain(email: string): string | null { if (!isValidEmail(email)) { return null; } const parts = email.split('@'); return parts[1].toLowerCase(); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kesslerio/attio-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server