Nodemailer email sending tested with jsonTransport: working versions (tested 2026-10-15)
Send multipart (HTML + text) emails with attachments through Nodemailer, swapping SMTP for jsonTransport in tests so nothing leaves the machine.
passed2 passed · 0 failed2026-10-15 13:31 UTC- Status
- passed
- Last passed
- 2026-10-15 13:31 UTC
- Tests
- 2 / 2
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions4
nodemailer@10.1.1tsx@4.23.15@types/node@24.19.2@types/nodemailer@8.0.2
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38742087302
The code
Exactly the files that passed. Your agent can pull them with get_recipe("nodemailer-templates-test-transport") (install), or see them on GitHub.
package.json
{
"name": "nodemailer-templates-test-transport",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"nodemailer": "10.1.1"
},
"devDependencies": {
"tsx": "4.23.15",
"@types/node": "24.19.2",
"@types/nodemailer": "8.0.2"
}
}
src/mailer.ts
import nodemailer, { type Transporter } from "nodemailer";
// prod: nodemailer.createTransport({ host, port: 587, secure: false, auth: { user, pass } })
export const testTransport = () => nodemailer.createTransport({ jsonTransport: true });
export async function sendInvoice(t: Transporter, to: string, invoiceNo: string, pdf: Buffer) {
const info = await t.sendMail({
from: '"Billing" <[email protected]>',
to,
subject: `Invoice ${invoiceNo}`,
text: `Your invoice ${invoiceNo} is attached.`,
html: `<p>Your invoice <b>${invoiceNo}</b> is attached.</p>`,
attachments: [{ filename: `${invoiceNo}.pdf`, content: pdf, contentType: "application/pdf" }],
headers: { "X-Invoice": invoiceNo },
});
return info;
}
test/mailer.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { sendInvoice, testTransport } from "../src/mailer.ts";
test("builds a multipart message with attachment", async () => {
const info: any = await sendInvoice(testTransport(), "[email protected]", "INV-7", Buffer.from("%PDF-1.4"));
const msg = JSON.parse(info.message);
assert.equal(msg.subject, "Invoice INV-7");
assert.deepEqual(msg.to.map((a: any) => a.address), ["[email protected]"]);
assert.match(msg.html, /<b>INV-7<\/b>/);
assert.ok(msg.text);
assert.equal(msg.attachments[0].filename, "INV-7.pdf");
assert.equal(Buffer.from(msg.attachments[0].content, "base64").toString(), "%PDF-1.4");
const headers = Object.fromEntries(Object.entries(msg.headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
assert.equal(headers["x-invoice"], "INV-7");
});
test("envelope has the right sender and recipients", async () => {
const info: any = await sendInvoice(testTransport(), "[email protected]", "INV-8", Buffer.alloc(1));
assert.deepEqual(info.envelope, { from: "[email protected]", to: ["[email protected]"] });
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.