Skip to content
Writing
MCPTypeScriptDevToolsTutorial

Building a Custom TypeScript MCP Server for Email: Zero to Production in 100 Lines

Learn how to create a high-performance, type-safe TypeScript MCP server that exposes email sending, domain DNS verification, and delivery log inspection to Claude and Cursor.

Why Build a Custom Email MCP Server?

Autonomous coding assistants like Claude Desktop, Cursor, and Windsurf achieve their greatest utility when granted access to real-world services. However, giving an LLM raw bash access to run curl commands exposes system credentials and lacks schema validation.

A custom Model Context Protocol (MCP) server exposes strictly typed, scoped tools directly into the assistant UI. In this guide, we will build a complete TypeScript MCP server implementing two tools: send_transactional_email and check_domain_dns.

1. Project Initialization & Dependencies

Initialize a clean project using modern TypeScript and install the official Anthropic MCP server package and Zod validation library.

BASH
mkdir sadasend-email-mcp && cd sadasend-email-mcp
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D typescript @types/node tsx

2. Complete Server Implementation (server.ts)

Here is the complete implementation utilizing StdioServerTransport for native integration with Claude Desktop and Cursor.

TYPESCRIPT
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({
  name: 'sadasend-email-mcp',
  version: '2.0.0',
});

// Tool 1: Scoped Outbound Email Dispatch
server.tool(
  'send_transactional_email',
  'Dispatches transactional email via SadaSend with edge recipient allowlist enforcement.',
  {
    to: z.string().email().describe('Destination email address'),
    subject: z.string().min(1).max(120).describe('Email subject line'),
    body: z.string().min(1).describe('Plain-text body content'),
  },
  async ({ to, subject, body }) => {
    const apiKey = process.env.SADASEND_API_KEY;
    if (!apiKey) {
      return {
        isError: true,
        content: [{ type: 'text', text: 'Configuration Error: SADASEND_API_KEY environment variable is not set.' }],
      };
    }

    const res = await fetch('https://api.sadasend.com/emails', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ to, subject, text: body }),
    });

    const data = await res.json();
    if (!res.ok) {
      return {
        isError: true,
        content: [{ type: 'text', text: `Send Refused (${res.status}): ${data.message || 'Unauthorized'}` }],
      };
    }

    return {
      content: [{ type: 'text', text: `Email successfully queued. Message ID: ${data.id}` }],
    };
  }
);

// Tool 2: Inspect Sending Domain SPF/DKIM Health
server.tool(
  'check_domain_dns',
  'Inspects SPF, DKIM, and DMARC verification status for a sending domain.',
  { domain: z.string().describe('The domain name to verify (e.g. acme.com)') },
  async ({ domain }) => {
    const res = await fetch(`https://api.sadasend.com/domains/${encodeURIComponent(domain)}/verify`);
    const data = await res.json();
    return {
      content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
    };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error('[MCP] SadaSend Server listening on stdio');
}

main().catch(console.error);

3. Connecting to Claude Desktop (claude_desktop_config.json)

Add your server configuration to ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows:

JSON
{
  "mcpServers": {
    "sadasend": {
      "command": "npx",
      "args": ["-y", "tsx", "/ABSOLUTE/PATH/TO/server.ts"],
      "env": {
        "SADASEND_API_KEY": "sada_live_sk_your_key_here"
      }
    }
  }
}
Free plan

Building AI agents that send email?

Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with ten tools — on the free plan, without a card.