mailtruss-mcp 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +9 -0
- package/README.md +20 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +13 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +61 -0
- package/package.json +54 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mailtruss
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
-
#
|
|
1
|
+
# mailtruss-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server for [Mailtruss](https://mailtruss.com).
|
|
4
|
+
|
|
5
|
+
Lets an AI assistant (Claude, Cursor, any MCP client) send email and manage domains, contacts, templates and broadcasts with **your** API key.
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"mailtruss": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": ["-y", "mailtruss-mcp"],
|
|
13
|
+
"env": { "MAILTRUSS_API_KEY": "ms_live_…", "MAILTRUSS_API_URL": "https://mailtruss.com" }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Create the key under **API keys** in the dashboard. Use a *full access* key for everything, or a *sending access* key to allow only `send_email`.
|
|
20
|
+
- Set `MAILTRUSS_MCP_READONLY=1` to expose only tools that cannot change anything.
|
|
21
|
+
- Tools that send, change or delete are annotated so your assistant's host can ask for confirmation first. `send_email` and `send_broadcast` deliver real mail.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { MailClient } from "mailtruss";
|
|
4
|
+
import { createServer } from "./server.js";
|
|
5
|
+
const apiKey = process.env.MAILTRUSS_API_KEY;
|
|
6
|
+
if (!apiKey) {
|
|
7
|
+
console.error("Set MAILTRUSS_API_KEY to an API key from your dashboard (API keys).");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
const client = new MailClient({ apiKey, baseUrl: process.env.MAILTRUSS_API_URL ?? "https://mailtruss.com" });
|
|
11
|
+
const readOnly = process.env.MAILTRUSS_MCP_READONLY === "1";
|
|
12
|
+
await createServer(client, { readOnly }).connect(new StdioServerTransport());
|
|
13
|
+
console.error(`mailtruss MCP server running${readOnly ? " (read-only)" : ""}`); // stderr: stdout carries the protocol
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { MailClient } from "mailtruss";
|
|
3
|
+
export type ServerOptions = {
|
|
4
|
+
readOnly?: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* MCP tools over the public API, using the caller's own API key. Every tool is annotated so an assistant's host can ask
|
|
8
|
+
* before anything that sends, changes or deletes. `readOnly` registers only the tools that cannot change anything.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createServer(client: MailClient, opts?: ServerOptions): McpServer;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { MailError } from "mailtruss";
|
|
4
|
+
/**
|
|
5
|
+
* MCP tools over the public API, using the caller's own API key. Every tool is annotated so an assistant's host can ask
|
|
6
|
+
* before anything that sends, changes or deletes. `readOnly` registers only the tools that cannot change anything.
|
|
7
|
+
*/
|
|
8
|
+
export function createServer(client, opts = {}) {
|
|
9
|
+
const server = new McpServer({ name: "mailtruss", version: "0.1.0" });
|
|
10
|
+
const text = (v) => ({ content: [{ type: "text", text: JSON.stringify(v, null, 2) }] });
|
|
11
|
+
const fail = (e) => ({ isError: true, content: [{ type: "text", text: e instanceof MailError ? `${e.message} (${e.statusCode} ${e.name_}${e.requestId ? `, request ${e.requestId}` : ""})` : e instanceof Error ? e.message : "Unexpected error" }] });
|
|
12
|
+
function tool(name, title, description, kind, shape, run) {
|
|
13
|
+
if (opts.readOnly && kind !== "read")
|
|
14
|
+
return;
|
|
15
|
+
server.registerTool(name, {
|
|
16
|
+
title, description, inputSchema: shape,
|
|
17
|
+
annotations: { readOnlyHint: kind === "read", destructiveHint: kind === "danger", idempotentHint: kind === "read", openWorldHint: name === "send_email" || name === "send_broadcast" },
|
|
18
|
+
}, (async (a) => { try {
|
|
19
|
+
return text(await run(a));
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
return fail(e);
|
|
23
|
+
} }));
|
|
24
|
+
}
|
|
25
|
+
const id = { id: z.string().describe("The id") };
|
|
26
|
+
// ---- Email ----
|
|
27
|
+
tool("send_email", "Send an email", "Sends one email now, or at `scheduled_at`. The from address must be on a verified domain. This delivers real mail to real recipients.", "danger", { from: z.string().describe("Sender, e.g. 'Ada <ada@yourdomain.com>'"), to: z.union([z.string(), z.array(z.string())]), subject: z.string(), html: z.string().optional(), text: z.string().optional(), reply_to: z.string().optional(), scheduled_at: z.string().optional().describe("ISO 8601, up to 30 days ahead") }, (a) => client.emails.send(a));
|
|
28
|
+
tool("get_email", "Get an email", "Returns one sent email with its latest status.", "read", id, (a) => client.emails.get(a.id));
|
|
29
|
+
tool("list_emails", "List emails", "Lists recent emails, newest first.", "read", { limit: z.number().int().min(1).max(100).optional(), after: z.string().optional().describe("Email id to continue after") }, (a) => client.emails.list(a));
|
|
30
|
+
tool("cancel_email", "Cancel a scheduled email", "Cancels an email that has not been sent yet.", "danger", id, (a) => client.emails.cancel(a.id));
|
|
31
|
+
// ---- Domains ----
|
|
32
|
+
tool("list_domains", "List domains", "Lists sending domains and their verification status.", "read", {}, () => client.domains.list());
|
|
33
|
+
tool("get_domain", "Get a domain", "Returns a domain with the DNS records that must be published.", "read", id, (a) => client.domains.get(a.id));
|
|
34
|
+
tool("create_domain", "Add a domain", "Adds a sending domain and returns the DNS records to publish.", "write", { name: z.string().describe("e.g. mail.example.com") }, (a) => client.domains.create(a));
|
|
35
|
+
tool("verify_domain", "Re-check a domain", "Checks the domain's DNS now.", "write", id, (a) => client.domains.verify(a.id));
|
|
36
|
+
tool("set_domain_tracking", "Set click and open tracking", "Turns click and open tracking on or off for a domain. Both are off by default; recipients must be told.", "write", { id: z.string(), click_tracking: z.boolean().optional(), open_tracking: z.boolean().optional() }, ({ id: i, ...p }) => client.domains.setTracking(i, p));
|
|
37
|
+
// ---- Audience ----
|
|
38
|
+
tool("list_contacts", "List contacts", "Lists contacts, optionally filtered.", "read", { q: z.string().optional(), tag: z.string().optional(), status: z.enum(["subscribed", "unsubscribed"]).optional(), page: z.number().int().min(1).optional(), limit: z.number().int().min(1).max(200).optional() }, (a) => client.contacts.list(a));
|
|
39
|
+
tool("get_contact", "Get a contact", "Returns a contact by id or email address.", "read", { id: z.string().describe("Contact id or email") }, (a) => client.contacts.get(a.id));
|
|
40
|
+
tool("create_contact", "Create or update a contact", "Creates a contact, or updates it when the email already exists. Never re-subscribes someone who unsubscribed.", "write", { email: z.string(), first_name: z.string().optional(), last_name: z.string().optional(), tags: z.array(z.string()).optional() }, (a) => client.contacts.create(a));
|
|
41
|
+
tool("delete_contact", "Delete a contact", "Permanently deletes a contact.", "danger", { id: z.string().describe("Contact id or email") }, (a) => client.contacts.remove(a.id));
|
|
42
|
+
tool("list_segments", "List segments", "Lists saved audience filters with their sizes.", "read", {}, () => client.segments.list());
|
|
43
|
+
tool("list_topics", "List topics", "Lists topics people can subscribe to.", "read", {}, () => client.topics.list());
|
|
44
|
+
tool("list_suppressions", "List suppressed addresses", "Lists addresses that will not be sent to, with the reason.", "read", { q: z.string().optional(), reason: z.enum(["hard_bounce", "complaint", "unsubscribe", "manual"]).optional() }, (a) => client.suppressions.list(a));
|
|
45
|
+
tool("suppress_address", "Suppress an address", "Stops all sending to an address.", "write", { email: z.string() }, (a) => client.suppressions.add(a.email));
|
|
46
|
+
tool("remove_suppression", "Remove a suppression", "Allows sending to a bounced or manually added address again. Complaints and unsubscribes cannot be removed.", "danger", { id: z.string().describe("Suppression id or email") }, (a) => client.suppressions.remove(a.id));
|
|
47
|
+
// ---- Templates and broadcasts ----
|
|
48
|
+
tool("list_templates", "List templates", "Lists email templates.", "read", {}, () => client.templates.list());
|
|
49
|
+
tool("get_template", "Get a template", "Returns a template's latest HTML, text and variables.", "read", id, (a) => client.templates.get(a.id));
|
|
50
|
+
tool("create_template", "Create a template", "Creates a template; set `publish` to make it live for sends by id or alias.", "write", { name: z.string(), subject: z.string(), html: z.string(), text: z.string().optional(), alias: z.string().optional(), publish: z.boolean().optional() }, (a) => client.templates.create(a));
|
|
51
|
+
tool("update_template", "Update a template", "Saves a new, unpublished version.", "write", { id: z.string(), name: z.string().optional(), subject: z.string().optional(), html: z.string().optional(), text: z.string().optional() }, ({ id: i, ...p }) => client.templates.update(i, p));
|
|
52
|
+
tool("publish_template", "Publish a template version", "Makes a version live for sends. Defaults to the latest.", "write", { id: z.string(), version: z.number().int().optional() }, ({ id: i, version }) => client.templates.publish(i, { version }));
|
|
53
|
+
tool("list_broadcasts", "List broadcasts", "Lists broadcasts with their status.", "read", {}, () => client.broadcasts.list());
|
|
54
|
+
tool("get_broadcast", "Get a broadcast", "Returns a broadcast with delivery, open and click counts.", "read", id, (a) => client.broadcasts.get(a.id));
|
|
55
|
+
tool("create_broadcast", "Create a draft broadcast", "Creates a draft. Nothing is sent until send_broadcast.", "write", { name: z.string(), from: z.string(), subject: z.string(), html: z.string(), segment_id: z.string().optional(), topic_id: z.string().optional() }, (a) => client.broadcasts.create(a));
|
|
56
|
+
tool("send_broadcast", "Send a broadcast", "Sends a draft broadcast to its whole audience now, or at `scheduled_at`. This delivers real mail to many people.", "danger", { id: z.string(), scheduled_at: z.string().optional() }, ({ id: i, scheduled_at }) => client.broadcasts.send(i, { scheduled_at }));
|
|
57
|
+
// ---- Operations ----
|
|
58
|
+
tool("list_webhooks", "List webhook endpoints", "Lists webhook endpoints and their events.", "read", {}, () => client.webhooks.list());
|
|
59
|
+
tool("list_logs", "List API request logs", "Lists recent API requests with status and duration. Useful for debugging.", "read", { limit: z.number().int().min(1).max(100).optional() }, (a) => client.logs.list(a));
|
|
60
|
+
return server;
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailtruss-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Mailtruss: lets AI assistants send email and manage domains, contacts, templates and broadcasts with your API key",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Mailtruss <hello@mailtruss.com>",
|
|
7
|
+
"homepage": "https://mailtruss.com/docs",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"mcp",
|
|
11
|
+
"model-context-protocol",
|
|
12
|
+
"email",
|
|
13
|
+
"mailtruss",
|
|
14
|
+
"ai-agents"
|
|
15
|
+
],
|
|
16
|
+
"main": "./dist/server.js",
|
|
17
|
+
"types": "./dist/server.d.ts",
|
|
18
|
+
"bin": {
|
|
19
|
+
"mailtruss-mcp": "./dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/server.d.ts",
|
|
24
|
+
"import": "./dist/server.js",
|
|
25
|
+
"default": "./dist/server.js"
|
|
26
|
+
},
|
|
27
|
+
"./package.json": "./package.json"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.32.0",
|
|
39
|
+
"zod": "^4.1.0",
|
|
40
|
+
"mailtruss": "^0.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^5.9.0",
|
|
44
|
+
"vitest": "^3.2.0",
|
|
45
|
+
"@types/node": "^22.0.0",
|
|
46
|
+
"tsx": "^4.20.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsc -p tsconfig.build.json",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"start": "tsx src/cli.ts"
|
|
56
|
+
}
|
|
6
57
|
}
|