Skip to main content
Glama
index.js13.3 kB
#!/usr/bin/env node // src/index.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; var API_KEY = process.env.GETMAILER_API_KEY; var API_URL = process.env.GETMAILER_API_URL || "https://getmailer.app"; if (!API_KEY) { console.error("GETMAILER_API_KEY environment variable is required"); process.exit(1); } async function apiRequest(endpoint, options = {}) { const url = `${API_URL}${endpoint}`; const response = await fetch(url, { ...options, headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, ...options.headers } }); if (!response.ok) { let errorMessage = response.statusText; try { const errorData = await response.json(); errorMessage = errorData.error || errorData.message || errorMessage; } catch { } throw new Error(`API Error: ${errorMessage}`); } return response.json(); } var server = new Server( { name: "getmailer-mcp", version: "1.0.0" }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "send_email", description: "Send a transactional email via GetMailer", inputSchema: { type: "object", properties: { from: { type: "string", description: "Sender email address (must be from a verified domain)" }, to: { type: "array", items: { type: "string" }, description: "Recipient email address(es)" }, subject: { type: "string", description: "Email subject line" }, html: { type: "string", description: "HTML content of the email" }, text: { type: "string", description: "Plain text content of the email" }, cc: { type: "array", items: { type: "string" }, description: "CC recipients (optional)" }, bcc: { type: "array", items: { type: "string" }, description: "BCC recipients (optional)" }, replyTo: { type: "string", description: "Reply-to address (optional)" }, templateId: { type: "string", description: "Template ID to use instead of html/text (optional)" }, variables: { type: "object", description: "Template variables as key-value pairs (optional)" } }, required: ["from", "to", "subject"] } }, { name: "list_emails", description: "List sent emails with status information", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of emails to return (default: 20)" }, cursor: { type: "string", description: "Pagination cursor for next page" } } } }, { name: "get_email", description: "Get details of a specific email including delivery events", inputSchema: { type: "object", properties: { id: { type: "string", description: "Email ID" } }, required: ["id"] } }, { name: "list_templates", description: "List available email templates", inputSchema: { type: "object", properties: {} } }, { name: "create_template", description: "Create a new email template", inputSchema: { type: "object", properties: { name: { type: "string", description: "Template name" }, subject: { type: "string", description: "Email subject (can include {{variables}})" }, html: { type: "string", description: "HTML content (can include {{variables}})" }, text: { type: "string", description: "Plain text content (optional)" } }, required: ["name", "subject", "html"] } }, { name: "list_domains", description: "List verified sending domains", inputSchema: { type: "object", properties: {} } }, { name: "add_domain", description: "Add a new sending domain (returns DNS records to configure)", inputSchema: { type: "object", properties: { domain: { type: "string", description: "Domain name to add (e.g., example.com)" } }, required: ["domain"] } }, { name: "verify_domain", description: "Check if a domain has been verified", inputSchema: { type: "object", properties: { id: { type: "string", description: "Domain ID" } }, required: ["id"] } }, { name: "get_analytics", description: "Get email analytics and statistics", inputSchema: { type: "object", properties: { type: { type: "string", enum: ["summary", "daily"], description: "Type of analytics (summary or daily)" }, days: { type: "number", description: "Number of days for daily stats (default: 30)" } } } }, { name: "list_suppression", description: "List suppressed email addresses (bounced, complained, or manually added)", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of entries to return (default: 50)" } } } }, { name: "add_to_suppression", description: "Add email addresses to the suppression list", inputSchema: { type: "object", properties: { emails: { type: "array", items: { type: "string" }, description: "Email addresses to suppress" }, reason: { type: "string", enum: ["MANUAL", "BOUNCE", "COMPLAINT"], description: "Reason for suppression (default: MANUAL)" } }, required: ["emails"] } }, { name: "create_batch", description: "Create a batch email job to send to multiple recipients", inputSchema: { type: "object", properties: { name: { type: "string", description: "Batch job name" }, from: { type: "string", description: "Sender email address" }, subject: { type: "string", description: "Email subject (can include {{variables}})" }, html: { type: "string", description: "HTML content (can include {{variables}})" }, text: { type: "string", description: "Plain text content (optional)" }, templateId: { type: "string", description: "Template ID to use instead of html/text (optional)" }, recipients: { type: "array", items: { type: "object", properties: { to: { type: "string" }, variables: { type: "object" } }, required: ["to"] }, description: "Array of recipients with optional per-recipient variables" }, replyTo: { type: "string", description: "Reply-to address (optional)" } }, required: ["name", "from", "recipients"] } }, { name: "list_batches", description: "List batch email jobs", inputSchema: { type: "object", properties: {} } }, { name: "get_batch", description: "Get batch job status and progress", inputSchema: { type: "object", properties: { id: { type: "string", description: "Batch ID" } }, required: ["id"] } } ] })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "send_email": { const result = await apiRequest( "/api/emails", { method: "POST", body: JSON.stringify(args) } ); return { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] }; } case "list_emails": { const params = new URLSearchParams(); if (args?.limit) params.set("limit", String(args.limit)); if (args?.cursor) params.set("cursor", String(args.cursor)); const result = await apiRequest(`/api/emails?${params}`); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "get_email": { const result = await apiRequest(`/api/emails/${args?.id}`); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "list_templates": { const result = await apiRequest("/api/templates"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "create_template": { const result = await apiRequest("/api/templates", { method: "POST", body: JSON.stringify(args) }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "list_domains": { const result = await apiRequest("/api/domains"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "add_domain": { const result = await apiRequest("/api/domains", { method: "POST", body: JSON.stringify({ domain: args?.domain }) }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "verify_domain": { const result = await apiRequest("/api/domains/verify", { method: "POST", body: JSON.stringify({ id: args?.id }) }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "get_analytics": { const type = args?.type || "summary"; const days = args?.days || 30; const result = await apiRequest(`/api/analytics?type=${type}&days=${days}`); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "list_suppression": { const params = new URLSearchParams(); if (args?.limit) params.set("limit", String(args.limit)); const result = await apiRequest(`/api/suppression?${params}`); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "add_to_suppression": { const result = await apiRequest("/api/suppression", { method: "POST", body: JSON.stringify({ emails: args?.emails, reason: args?.reason || "MANUAL" }) }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "create_batch": { const result = await apiRequest("/api/batch", { method: "POST", body: JSON.stringify(args) }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "list_batches": { const result = await apiRequest("/api/batch"); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } case "get_batch": { const result = await apiRequest(`/api/batch/${args?.id}`); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; return { content: [{ type: "text", text: `Error: ${message}` }], isError: true }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("GetMailer MCP server running"); } main().catch((error) => { console.error("Server error:", error); process.exit(1); });

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/getplatform/getmailer-mcp'

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