-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathindex.ts
185 lines (162 loc) · 5.37 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { Resend } from "resend";
import minimist from "minimist";
// Parse command line arguments
const argv = minimist(process.argv.slice(2));
// Get API key from command line argument or fall back to environment variable
const apiKey = argv.key || process.env.RESEND_API_KEY;
// Get sender email address from command line argument or fall back to environment variable
// Optional.
const senderEmailAddress = argv.sender || process.env.SENDER_EMAIL_ADDRESS;
// Get reply to email addresses from command line argument or fall back to environment variable
let replierEmailAddresses: string[] = [];
if (Array.isArray(argv["reply-to"])) {
replierEmailAddresses = argv["reply-to"];
} else if (typeof argv["reply-to"] === "string") {
replierEmailAddresses = [argv["reply-to"]];
} else if (process.env.REPLY_TO_EMAIL_ADDRESSES) {
replierEmailAddresses = process.env.REPLY_TO_EMAIL_ADDRESSES.split(",");
}
if (!apiKey) {
console.error(
"No API key provided. Please set RESEND_API_KEY environment variable or use --key argument"
);
process.exit(1);
}
const resend = new Resend(apiKey);
// Create server instance
const server = new McpServer({
name: "email-sending-service",
version: "1.0.0",
});
server.tool(
"send-email",
"Send an email using Resend",
{
to: z.string().email().describe("Recipient email address"),
subject: z.string().describe("Email subject line"),
text: z.string().describe("Plain text email content"),
html: z
.string()
.optional()
.describe(
"HTML email content. When provided, the plain text argument MUST be provided as well."
),
cc: z
.string()
.email()
.array()
.optional()
.describe("Optional array of CC email addresses. You MUST ask the user for this parameter. Under no circumstance provide it yourself"),
bcc: z
.string()
.email()
.array()
.optional()
.describe("Optional array of BCC email addresses. You MUST ask the user for this parameter. Under no circumstance provide it yourself"),
scheduledAt: z
.string()
.optional()
.describe(
"Optional parameter to schedule the email. This uses natural language. Examples would be 'tomorrow at 10am' or 'in 2 hours' or 'next day at 9am PST' or 'Friday at 3pm ET'."
),
// If sender email address is not provided, the tool requires it as an argument
...(!senderEmailAddress
? {
from: z
.string()
.email()
.nonempty()
.describe(
"Sender email address. You MUST ask the user for this parameter. Under no circumstance provide it yourself"
),
}
: {}),
...(replierEmailAddresses.length === 0
? {
replyTo: z
.string()
.email()
.array()
.optional()
.describe(
"Optional email addresses for the email readers to reply to. You MUST ask the user for this parameter. Under no circumstance provide it yourself"
),
}
: {}),
},
async ({ from, to, subject, text, html, replyTo, scheduledAt, cc, bcc }) => {
const fromEmailAddress = from ?? senderEmailAddress;
const replyToEmailAddresses = replyTo ?? replierEmailAddresses;
// Type check on from, since "from" is optionally included in the arguments schema
// This should never happen.
if (typeof fromEmailAddress !== "string") {
throw new Error("from argument must be provided.");
}
// Similar type check for "reply-to" email addresses.
if (
typeof replyToEmailAddresses !== "string" &&
!Array.isArray(replyToEmailAddresses)
) {
throw new Error("replyTo argument must be provided.");
}
console.error(`Debug - Sending email with from: ${fromEmailAddress}`);
// Explicitly structure the request with all parameters to ensure they're passed correctly
const emailRequest: {
to: string;
subject: string;
text: string;
from: string;
replyTo: string | string[];
html?: string;
scheduledAt?: string;
cc?: string[];
bcc?: string[];
} = {
to,
subject,
text,
from: fromEmailAddress,
replyTo: replyToEmailAddresses,
};
// Add optional parameters conditionally
if (html) {
emailRequest.html = html;
}
if (scheduledAt) {
emailRequest.scheduledAt = scheduledAt;
}
if (cc) {
emailRequest.cc = cc;
}
if (bcc) {
emailRequest.bcc = bcc;
}
console.error(`Email request: ${JSON.stringify(emailRequest)}`);
const response = await resend.emails.send(emailRequest);
if (response.error) {
throw new Error(
`Email failed to send: ${JSON.stringify(response.error)}`
);
}
return {
content: [
{
type: "text",
text: `Email sent successfully! ${JSON.stringify(response.data)}`,
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Email sending service MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});