send_email
Send emails via the Resend API with support for plain text, attachments, and optional scheduling. Specify custom sender and reply-to addresses to personalize your messages.
Instructions
Sends an email using the Resend API. Supports plain text content, attachments and optional scheduling. Can specify custom sender and reply-to addresses.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| attachments | No | Optional. List of attachments. Each attachment must have a filename and either localPath (path to a local file) or remoteUrl (URL to a file on the internet). | |
| content | Yes | Plain text email content | |
| from | No | Optional. If provided, uses this as the sender email address; otherwise uses SENDER_EMAIL_ADDRESS environment variable | |
| replyTo | No | Optional. If provided, uses these as the reply-to email addresses; otherwise uses REPLY_TO_EMAIL_ADDRESSES environment variable | |
| scheduledAt | No | Optional parameter to schedule the email. This uses natural language. Examples would be 'tomorrow at 10am' or 'in 2 hours' or 'next day at 9am PST' or 'Friday at 3pm ET'. | |
| subject | Yes | Email subject line | |
| to | Yes | Recipient email address |
Implementation Reference
- index.ts:194-255 (handler)The core handler logic for the 'send_email' tool within the CallToolRequestSchema handler. Validates input arguments, processes attachments from local paths or remote URLs, sends the email using the Resend API, and returns success or error response.case SEND_EMAIL_TOOL.name: { if (!isEmailArgs(args)) { throw new Error("Invalid arguments for send_email tool"); } const fromEmail = args.from || SENDER_EMAIL_ADDRESS.trim(); if (!fromEmail) { throw new Error("Sender email must be provided either via args or SENDER_EMAIL_ADDRESS environment variable"); } const replyToEmails = args.replyTo || REPLY_TO_EMAIL_ADDRESSES; // Convert attachments to Resend API format const attachments = args.attachments?.map(attachment => { if (attachment.localPath) { // Check if file exists if (!existsSync(attachment.localPath)) { throw new Error(`Attachment file not found: ${attachment.localPath}`); } // Try to read the file try { // readFileSync can read any file format as it reads files in binary mode const content = readFileSync(attachment.localPath).toString('base64'); return { filename: attachment.filename, content, path: undefined }; } catch (error) { throw new Error(`Failed to read attachment file: ${attachment.localPath}. Error: ${error instanceof Error ? error.message : String(error)}`); } } // If using remoteUrl return { filename: attachment.filename, content: undefined, path: attachment.remoteUrl }; }); const response = await resend.emails.send({ to: args.to, from: fromEmail, subject: args.subject, text: args.content, replyTo: replyToEmails, scheduledAt: args.scheduledAt, attachments, }); if (response.error) { throw new Error(`Failed to send email: ${JSON.stringify(response.error)}`); } return { content: [{ type: "text", text: `Email sent successfully! ${JSON.stringify(response.data)}` }] }; }
- index.ts:41-102 (schema)Input schema (JSON Schema) for the 'send_email' tool, defining parameters like to, subject, content, optional from, replyTo, scheduledAt, and attachments with validation rules.inputSchema: { type: "object", properties: { to: { type: "string", format: "email", description: "Recipient email address" }, subject: { type: "string", description: "Email subject line" }, content: { type: "string", description: "Plain text email content" }, from: { type: "string", format: "email", description: "Optional. If provided, uses this as the sender email address; otherwise uses SENDER_EMAIL_ADDRESS environment variable" }, replyTo: { type: "array", items: { type: "string", format: "email" }, description: "Optional. If provided, uses these as the reply-to email addresses; otherwise uses REPLY_TO_EMAIL_ADDRESSES environment variable" }, scheduledAt: { type: "string", description: "Optional parameter to schedule the email. This uses natural language. Examples would be 'tomorrow at 10am' or 'in 2 hours' or 'next day at 9am PST' or 'Friday at 3pm ET'." }, attachments: { type: "array", items: { type: "object", properties: { filename: { type: "string", description: "Name of the attachment file" }, localPath: { type: "string", description: "Absolute path to a local file on user's computer. Required if remoteUrl is not provided." }, remoteUrl: { type: "string", description: "URL to a file on the internet. Required if localPath is not provided." } }, required: ["filename"], oneOf: [ { required: ["localPath"] }, { required: ["remoteUrl"] } ] }, description: "Optional. List of attachments. Each attachment must have a filename and either localPath (path to a local file) or remoteUrl (URL to a file on the internet)." } }, required: ["to", "subject", "content"] }
- index.ts:181-183 (registration)Registration of the 'send_email' tool in the ListToolsRequestSchema handler, making it discoverable by clients.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [SEND_EMAIL_TOOL], }));
- index.ts:124-165 (helper)Type guard function 'isEmailArgs' used to validate the arguments passed to the send_email handler.function isEmailArgs(args: unknown): args is { to: string; subject: string; content: string; from?: string; replyTo?: string[]; scheduledAt?: string; attachments?: Attachment[]; } { if ( typeof args !== "object" || args === null ) { return false; } const emailArgs = args as { to: unknown; subject: unknown; content: unknown; attachments?: unknown[]; }; if ( !("to" in emailArgs) || typeof emailArgs.to !== "string" || !("subject" in emailArgs) || typeof emailArgs.subject !== "string" || !("content" in emailArgs) || typeof emailArgs.content !== "string" ) { return false; } // Check optional attachments if present if ("attachments" in emailArgs) { if (!Array.isArray(emailArgs.attachments)) return false; if (!emailArgs.attachments.every(isAttachment)) return false; } return true; }
- index.ts:112-122 (helper)Type guard function 'isAttachment' used to validate individual attachment objects in the send_email handler.function isAttachment(arg: unknown): arg is Attachment { if (typeof arg !== "object" || arg === null) return false; const attachment = arg as Attachment; if (typeof attachment.filename !== "string") return false; // Must have either localPath or remoteUrl, but not both const hasLocalPath = "localPath" in attachment && typeof attachment.localPath === "string"; const hasRemoteUrl = "remoteUrl" in attachment && typeof attachment.remoteUrl === "string"; return hasLocalPath !== hasRemoteUrl; // XOR operation }