TypeScript
The @mcpmailer/sdk client: install, send, read the inbox, and open the vault.
The TypeScript SDK is a thin, fully typed wrapper over the v1 API with no runtime dependencies. It uses the global fetch, so it runs unchanged on Node 18 and up, Bun, Deno, and Cloudflare Workers.
npm install @mcpmailer/sdkThe client
One class, one method per call, no local state. Pass your own fetch if you need to route requests through a proxy or a test double.
import { Mcpmailer } from '@mcpmailer/sdk';
// Reads MCPMAILER_API_KEY when you pass nothing.
const mm = new Mcpmailer();
// Or be explicit, and point at another deployment if you need to.
const staging = new Mcpmailer({
apiKey: process.env.STAGING_KEY,
baseUrl: 'https://staging.mcpmailer.com'
});Sending
Attachments take raw bytes or a base64 string; the SDK encodes them for you. Set style to plain, flat, or card to override how the message looks, or leave it out and let the mailbox decide.
const result = await mm.send({
to: ['jane@acme.com'],
subject: 'Your quote',
body: 'Attached, as promised.',
attachments: [
{ filename: 'quote.pdf', content: await Bun.file('quote.pdf').bytes() }
],
trackOpens: true
});
if (result.status === 'rejected') {
// A refused send is a result, not a throw: reason is something to act on.
console.log(result.reason); // daily_send_quota_exhausted, recipient_suppressed, ...
}Reading and answering
The methods below cover the loop most agents actually run: read what came in, find out who wrote, answer, record what you learned, and get it out of the inbox.
const inbox = await mm.listMessages({ unreadOnly: true, limit: 20 });
for (const message of inbox) {
const [who] = await mm.lookupContact(message.from);
await mm.replyAll(message.id, 'Thanks, looking into it now.');
if (who) {
await mm.rememberAboutContact(who.id, 'Asked about SSO pricing', message.id);
}
await mm.archiveMessage(message.id);
}Also on the client: getMessage, getThread, search, getAttachment, markUnread, forward, listNotes and createNote, listContacts and createContact, listMailboxes and createMailbox, listDomains, listWebhooks, createWebhook and updateWebhook.
The vault
Secrets this agent has been granted, read with the same key as everything else. Values are encrypted at rest and opened server-side for the agents that were granted them, so a credential can be used without ever passing through a prompt.
const secrets = await mm.listSecrets();
const stripe = await mm.getSecret(secrets[0].id);
// The value arrives ready to use, without ever passing through a prompt.
const { code, expiresInSeconds } = await mm.getTotpCode(secrets[0].id);Errors
McpmailerError carries the HTTP status, a stable code, and a hint meant to be read. A rejected send does not throw: check result.status instead.
import { McpmailerError } from '@mcpmailer/sdk';
try {
await mm.send({ to: ['jane@acme.com'], subject: 'Hi', body: 'Hello' });
} catch (error) {
if (error instanceof McpmailerError) {
console.log(error.status, error.code, error.hint);
}
}