@assistantmail/assistantmail-mcp 1.2.0 → 1.2.2
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/README.md +141 -134
- package/package.json +12 -3
- package/src/index.js +838 -838
package/src/index.js
CHANGED
|
@@ -1,838 +1,838 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import process from 'node:process';
|
|
3
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
|
|
7
|
-
const apiBaseUrl = process.env.ASSISTANT_MAIL_API_BASE_URL ?? 'https://api.assistant-mail.ai';
|
|
8
|
-
const defaultApiKey = process.env.ASSISTANT_MAIL_API_KEY ?? '';
|
|
9
|
-
|
|
10
|
-
const server = new McpServer({
|
|
11
|
-
name: 'assistantmail-mcp',
|
|
12
|
-
version: '
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
function resolveApiKey(inputApiKey) {
|
|
16
|
-
const key = (inputApiKey ?? '').trim() || defaultApiKey.trim();
|
|
17
|
-
return key.startsWith('amk_') ? key : null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function assistantMailRequest({ method, path, apiKey, query = {}, body }) {
|
|
21
|
-
const key = resolveApiKey(apiKey);
|
|
22
|
-
if (!key) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
'Missing API key. Provide apiKey in tool input or set ASSISTANT_MAIL_API_KEY in the MCP server environment.',
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const url = new URL(path, apiBaseUrl);
|
|
29
|
-
for (const [k, v] of Object.entries(query)) {
|
|
30
|
-
if (v !== undefined && v !== null && `${v}`.length > 0) {
|
|
31
|
-
url.searchParams.set(k, `${v}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const headers = {
|
|
36
|
-
Accept: 'application/json',
|
|
37
|
-
'x-api-key': key,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
if (body !== undefined) {
|
|
41
|
-
headers['Content-Type'] = 'application/json';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const res = await fetch(url, {
|
|
45
|
-
method,
|
|
46
|
-
headers,
|
|
47
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const raw = await res.text();
|
|
51
|
-
let parsed = null;
|
|
52
|
-
try {
|
|
53
|
-
parsed = raw ? JSON.parse(raw) : null;
|
|
54
|
-
} catch {
|
|
55
|
-
parsed = { raw };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!res.ok) {
|
|
59
|
-
const message = typeof parsed?.message === 'string'
|
|
60
|
-
? parsed.message
|
|
61
|
-
: `AssistantMail API request failed (${res.status}).`;
|
|
62
|
-
throw new Error(message);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
data: parsed,
|
|
67
|
-
status: res.status,
|
|
68
|
-
request: {
|
|
69
|
-
method,
|
|
70
|
-
url: url.toString(),
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function assistantMailGet(path, apiKey, query = {}) {
|
|
76
|
-
return assistantMailRequest({ method: 'GET', path, apiKey, query });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function assistantMailPost(path, apiKey, body) {
|
|
80
|
-
return assistantMailRequest({ method: 'POST', path, apiKey, body });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function assistantMailPut(path, apiKey, body) {
|
|
84
|
-
return assistantMailRequest({ method: 'PUT', path, apiKey, body });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function assistantMailDelete(path, apiKey, body) {
|
|
88
|
-
return assistantMailRequest({ method: 'DELETE', path, apiKey, body });
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
server.tool(
|
|
92
|
-
'assistantmail_health',
|
|
93
|
-
'Returns AssistantMail MCP server and API endpoint metadata.',
|
|
94
|
-
{},
|
|
95
|
-
async () => ({
|
|
96
|
-
content: [
|
|
97
|
-
{
|
|
98
|
-
type: 'text',
|
|
99
|
-
text: `assistantmail-mcp is running. API base URL: ${apiBaseUrl}`,
|
|
100
|
-
},
|
|
101
|
-
],
|
|
102
|
-
structuredContent: {
|
|
103
|
-
status: 'ok',
|
|
104
|
-
apiBaseUrl,
|
|
105
|
-
},
|
|
106
|
-
}),
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
server.tool(
|
|
110
|
-
'assistantmail_get_me',
|
|
111
|
-
'Gets account profile and tier metadata for the authenticated account.',
|
|
112
|
-
{
|
|
113
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
114
|
-
},
|
|
115
|
-
async ({ apiKey }) => {
|
|
116
|
-
try {
|
|
117
|
-
const result = await assistantMailGet('/v1/users/me', apiKey);
|
|
118
|
-
return {
|
|
119
|
-
content: [
|
|
120
|
-
{
|
|
121
|
-
type: 'text',
|
|
122
|
-
text: `Retrieved account profile for ${result.data?.email ?? 'authenticated account'}.`,
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
structuredContent: result,
|
|
126
|
-
};
|
|
127
|
-
} catch (error) {
|
|
128
|
-
return {
|
|
129
|
-
content: [
|
|
130
|
-
{
|
|
131
|
-
type: 'text',
|
|
132
|
-
text: `assistantmail_get_me failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
isError: true,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
server.tool(
|
|
142
|
-
'assistantmail_get_inbound_policy',
|
|
143
|
-
'Gets inbound email policy settings for the authenticated account.',
|
|
144
|
-
{
|
|
145
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
146
|
-
},
|
|
147
|
-
async ({ apiKey }) => {
|
|
148
|
-
try {
|
|
149
|
-
const result = await assistantMailGet('/v1/inbound-policy', apiKey);
|
|
150
|
-
return {
|
|
151
|
-
content: [
|
|
152
|
-
{
|
|
153
|
-
type: 'text',
|
|
154
|
-
text: `Retrieved inbound policy (${result.data?.policy ?? 'unknown'}).`,
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
structuredContent: result,
|
|
158
|
-
};
|
|
159
|
-
} catch (error) {
|
|
160
|
-
return {
|
|
161
|
-
content: [
|
|
162
|
-
{
|
|
163
|
-
type: 'text',
|
|
164
|
-
text: `assistantmail_get_inbound_policy failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
isError: true,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
server.tool(
|
|
174
|
-
'assistantmail_update_inbound_policy',
|
|
175
|
-
'Updates inbound email policy for the authenticated account.',
|
|
176
|
-
{
|
|
177
|
-
policy: z.enum(['owner', 'list', 'sent']),
|
|
178
|
-
allowedSenders: z.array(z.string().email()).optional(),
|
|
179
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
180
|
-
},
|
|
181
|
-
async ({ policy, allowedSenders, apiKey }) => {
|
|
182
|
-
try {
|
|
183
|
-
const result = await assistantMailPut('/v1/inbound-policy', apiKey, {
|
|
184
|
-
policy,
|
|
185
|
-
allowedSenders,
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
content: [
|
|
189
|
-
{
|
|
190
|
-
type: 'text',
|
|
191
|
-
text: `Updated inbound policy to ${policy}.`,
|
|
192
|
-
},
|
|
193
|
-
],
|
|
194
|
-
structuredContent: result,
|
|
195
|
-
};
|
|
196
|
-
} catch (error) {
|
|
197
|
-
return {
|
|
198
|
-
content: [
|
|
199
|
-
{
|
|
200
|
-
type: 'text',
|
|
201
|
-
text: `assistantmail_update_inbound_policy failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
isError: true,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
server.tool(
|
|
211
|
-
'assistantmail_create_mailbox',
|
|
212
|
-
'Creates a mailbox for the authenticated account.',
|
|
213
|
-
{
|
|
214
|
-
displayName: z.string().min(1).max(120).optional(),
|
|
215
|
-
address: z.string().min(1).optional(),
|
|
216
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
217
|
-
},
|
|
218
|
-
async ({ displayName, address, apiKey }) => {
|
|
219
|
-
try {
|
|
220
|
-
const result = await assistantMailPost('/v1/mailboxes', apiKey, {
|
|
221
|
-
displayName,
|
|
222
|
-
address,
|
|
223
|
-
});
|
|
224
|
-
return {
|
|
225
|
-
content: [
|
|
226
|
-
{
|
|
227
|
-
type: 'text',
|
|
228
|
-
text: `Created mailbox ${result.data?.mailbox?.mailboxId ?? '(unknown id)'}.`,
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
structuredContent: result,
|
|
232
|
-
};
|
|
233
|
-
} catch (error) {
|
|
234
|
-
return {
|
|
235
|
-
content: [
|
|
236
|
-
{
|
|
237
|
-
type: 'text',
|
|
238
|
-
text: `assistantmail_create_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
isError: true,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
server.tool(
|
|
248
|
-
'assistantmail_list_mailboxes',
|
|
249
|
-
'Lists mailboxes accessible to the authenticated account.',
|
|
250
|
-
{
|
|
251
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
252
|
-
},
|
|
253
|
-
async ({ apiKey }) => {
|
|
254
|
-
try {
|
|
255
|
-
const result = await assistantMailGet('/v1/mailboxes', apiKey);
|
|
256
|
-
return {
|
|
257
|
-
content: [
|
|
258
|
-
{
|
|
259
|
-
type: 'text',
|
|
260
|
-
text: `Retrieved ${Array.isArray(result.data?.mailboxes) ? result.data.mailboxes.length : 0} mailbox(es).`,
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
structuredContent: result,
|
|
264
|
-
};
|
|
265
|
-
} catch (error) {
|
|
266
|
-
return {
|
|
267
|
-
content: [
|
|
268
|
-
{
|
|
269
|
-
type: 'text',
|
|
270
|
-
text: `assistantmail_list_mailboxes failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
271
|
-
},
|
|
272
|
-
],
|
|
273
|
-
isError: true,
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
server.tool(
|
|
280
|
-
'assistantmail_get_mailbox',
|
|
281
|
-
'Gets mailbox metadata for a mailbox ID.',
|
|
282
|
-
{
|
|
283
|
-
mailboxId: z.string().min(1),
|
|
284
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
285
|
-
},
|
|
286
|
-
async ({ mailboxId, apiKey }) => {
|
|
287
|
-
try {
|
|
288
|
-
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}`, apiKey);
|
|
289
|
-
return {
|
|
290
|
-
content: [
|
|
291
|
-
{
|
|
292
|
-
type: 'text',
|
|
293
|
-
text: `Retrieved mailbox ${mailboxId}.`,
|
|
294
|
-
},
|
|
295
|
-
],
|
|
296
|
-
structuredContent: result,
|
|
297
|
-
};
|
|
298
|
-
} catch (error) {
|
|
299
|
-
return {
|
|
300
|
-
content: [
|
|
301
|
-
{
|
|
302
|
-
type: 'text',
|
|
303
|
-
text: `assistantmail_get_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
304
|
-
},
|
|
305
|
-
],
|
|
306
|
-
isError: true,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
server.tool(
|
|
313
|
-
'assistantmail_update_mailbox',
|
|
314
|
-
'Updates mailbox metadata (currently display name).',
|
|
315
|
-
{
|
|
316
|
-
mailboxId: z.string().min(1),
|
|
317
|
-
displayName: z.string().min(1).max(120),
|
|
318
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
319
|
-
},
|
|
320
|
-
async ({ mailboxId, displayName, apiKey }) => {
|
|
321
|
-
try {
|
|
322
|
-
const result = await assistantMailPut(`/v1/mailboxes/${mailboxId}`, apiKey, {
|
|
323
|
-
displayName,
|
|
324
|
-
});
|
|
325
|
-
return {
|
|
326
|
-
content: [
|
|
327
|
-
{
|
|
328
|
-
type: 'text',
|
|
329
|
-
text: `Updated mailbox ${mailboxId}.`,
|
|
330
|
-
},
|
|
331
|
-
],
|
|
332
|
-
structuredContent: result,
|
|
333
|
-
};
|
|
334
|
-
} catch (error) {
|
|
335
|
-
return {
|
|
336
|
-
content: [
|
|
337
|
-
{
|
|
338
|
-
type: 'text',
|
|
339
|
-
text: `assistantmail_update_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
isError: true,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
);
|
|
347
|
-
|
|
348
|
-
server.tool(
|
|
349
|
-
'assistantmail_delete_mailbox',
|
|
350
|
-
'Deletes a mailbox and all associated messages.',
|
|
351
|
-
{
|
|
352
|
-
mailboxId: z.string().min(1),
|
|
353
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
354
|
-
},
|
|
355
|
-
async ({ mailboxId, apiKey }) => {
|
|
356
|
-
try {
|
|
357
|
-
const result = await assistantMailDelete(`/v1/mailboxes/${mailboxId}`, apiKey);
|
|
358
|
-
return {
|
|
359
|
-
content: [
|
|
360
|
-
{
|
|
361
|
-
type: 'text',
|
|
362
|
-
text: `Deleted mailbox ${mailboxId}.`,
|
|
363
|
-
},
|
|
364
|
-
],
|
|
365
|
-
structuredContent: result,
|
|
366
|
-
};
|
|
367
|
-
} catch (error) {
|
|
368
|
-
return {
|
|
369
|
-
content: [
|
|
370
|
-
{
|
|
371
|
-
type: 'text',
|
|
372
|
-
text: `assistantmail_delete_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
isError: true,
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
server.tool(
|
|
382
|
-
'assistantmail_list_messages',
|
|
383
|
-
'Lists inbound and outbound messages for a mailbox.',
|
|
384
|
-
{
|
|
385
|
-
mailboxId: z.string().min(1),
|
|
386
|
-
since: z.string().datetime().optional(),
|
|
387
|
-
limit: z.number().int().min(1).max(100).optional(),
|
|
388
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
389
|
-
},
|
|
390
|
-
async ({ mailboxId, since, limit, apiKey }) => {
|
|
391
|
-
try {
|
|
392
|
-
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
393
|
-
since,
|
|
394
|
-
limit,
|
|
395
|
-
});
|
|
396
|
-
return {
|
|
397
|
-
content: [
|
|
398
|
-
{
|
|
399
|
-
type: 'text',
|
|
400
|
-
text: `Retrieved ${Array.isArray(result.data?.messages) ? result.data.messages.length : 0} message(s) for mailbox ${mailboxId}.`,
|
|
401
|
-
},
|
|
402
|
-
],
|
|
403
|
-
structuredContent: result,
|
|
404
|
-
};
|
|
405
|
-
} catch (error) {
|
|
406
|
-
return {
|
|
407
|
-
content: [
|
|
408
|
-
{
|
|
409
|
-
type: 'text',
|
|
410
|
-
text: `assistantmail_list_messages failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
411
|
-
},
|
|
412
|
-
],
|
|
413
|
-
isError: true,
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
server.tool(
|
|
420
|
-
'assistantmail_get_message',
|
|
421
|
-
'Gets a specific message for a mailbox, including hydrated text/html bodies when available.',
|
|
422
|
-
{
|
|
423
|
-
mailboxId: z.string().min(1),
|
|
424
|
-
messageId: z.string().min(1),
|
|
425
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
426
|
-
},
|
|
427
|
-
async ({ mailboxId, messageId, apiKey }) => {
|
|
428
|
-
try {
|
|
429
|
-
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/messages/${messageId}`, apiKey);
|
|
430
|
-
return {
|
|
431
|
-
content: [
|
|
432
|
-
{
|
|
433
|
-
type: 'text',
|
|
434
|
-
text: `Retrieved message ${messageId} for mailbox ${mailboxId}.`,
|
|
435
|
-
},
|
|
436
|
-
],
|
|
437
|
-
structuredContent: result,
|
|
438
|
-
};
|
|
439
|
-
} catch (error) {
|
|
440
|
-
return {
|
|
441
|
-
content: [
|
|
442
|
-
{
|
|
443
|
-
type: 'text',
|
|
444
|
-
text: `assistantmail_get_message failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
445
|
-
},
|
|
446
|
-
],
|
|
447
|
-
isError: true,
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
server.tool(
|
|
454
|
-
'assistantmail_send_email',
|
|
455
|
-
'Queues an outbound email for a mailbox.',
|
|
456
|
-
{
|
|
457
|
-
mailboxId: z.string().min(1),
|
|
458
|
-
to: z.string().email(),
|
|
459
|
-
subject: z.string().min(1),
|
|
460
|
-
text: z.string().optional(),
|
|
461
|
-
html: z.string().optional(),
|
|
462
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
463
|
-
},
|
|
464
|
-
async ({ mailboxId, to, subject, text, html, apiKey }) => {
|
|
465
|
-
if (!text && !html) {
|
|
466
|
-
return {
|
|
467
|
-
content: [
|
|
468
|
-
{
|
|
469
|
-
type: 'text',
|
|
470
|
-
text: 'assistantmail_send_email failed: Provide at least one of text or html.',
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
isError: true,
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
try {
|
|
478
|
-
const result = await assistantMailPost(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
479
|
-
to,
|
|
480
|
-
subject,
|
|
481
|
-
text,
|
|
482
|
-
html,
|
|
483
|
-
});
|
|
484
|
-
return {
|
|
485
|
-
content: [
|
|
486
|
-
{
|
|
487
|
-
type: 'text',
|
|
488
|
-
text: `Queued email to ${to} from mailbox ${mailboxId}.`,
|
|
489
|
-
},
|
|
490
|
-
],
|
|
491
|
-
structuredContent: result,
|
|
492
|
-
};
|
|
493
|
-
} catch (error) {
|
|
494
|
-
return {
|
|
495
|
-
content: [
|
|
496
|
-
{
|
|
497
|
-
type: 'text',
|
|
498
|
-
text: `assistantmail_send_email failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
499
|
-
},
|
|
500
|
-
],
|
|
501
|
-
isError: true,
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
},
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
server.tool(
|
|
508
|
-
'assistantmail_delete_messages',
|
|
509
|
-
'Deletes messages from a mailbox by IDs or deletes all messages.',
|
|
510
|
-
{
|
|
511
|
-
mailboxId: z.string().min(1),
|
|
512
|
-
messageIds: z.array(z.string().min(1)).optional(),
|
|
513
|
-
deleteAll: z.boolean().optional(),
|
|
514
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
515
|
-
},
|
|
516
|
-
async ({ mailboxId, messageIds, deleteAll, apiKey }) => {
|
|
517
|
-
if (!deleteAll && (!Array.isArray(messageIds) || messageIds.length === 0)) {
|
|
518
|
-
return {
|
|
519
|
-
content: [
|
|
520
|
-
{
|
|
521
|
-
type: 'text',
|
|
522
|
-
text: 'assistantmail_delete_messages failed: Provide messageIds or set deleteAll to true.',
|
|
523
|
-
},
|
|
524
|
-
],
|
|
525
|
-
isError: true,
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
try {
|
|
530
|
-
const result = await assistantMailDelete(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
531
|
-
messageIds,
|
|
532
|
-
deleteAll,
|
|
533
|
-
});
|
|
534
|
-
return {
|
|
535
|
-
content: [
|
|
536
|
-
{
|
|
537
|
-
type: 'text',
|
|
538
|
-
text: `Deleted messages for mailbox ${mailboxId}.`,
|
|
539
|
-
},
|
|
540
|
-
],
|
|
541
|
-
structuredContent: result,
|
|
542
|
-
};
|
|
543
|
-
} catch (error) {
|
|
544
|
-
return {
|
|
545
|
-
content: [
|
|
546
|
-
{
|
|
547
|
-
type: 'text',
|
|
548
|
-
text: `assistantmail_delete_messages failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
549
|
-
},
|
|
550
|
-
],
|
|
551
|
-
isError: true,
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
);
|
|
556
|
-
|
|
557
|
-
server.tool(
|
|
558
|
-
'assistantmail_get_usage',
|
|
559
|
-
'Gets daily and monthly send quota usage for a mailbox.',
|
|
560
|
-
{
|
|
561
|
-
mailboxId: z.string().min(1),
|
|
562
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
563
|
-
},
|
|
564
|
-
async ({ mailboxId, apiKey }) => {
|
|
565
|
-
try {
|
|
566
|
-
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/usage`, apiKey);
|
|
567
|
-
return {
|
|
568
|
-
content: [
|
|
569
|
-
{
|
|
570
|
-
type: 'text',
|
|
571
|
-
text: `Retrieved usage for mailbox ${mailboxId}.`,
|
|
572
|
-
},
|
|
573
|
-
],
|
|
574
|
-
structuredContent: result,
|
|
575
|
-
};
|
|
576
|
-
} catch (error) {
|
|
577
|
-
return {
|
|
578
|
-
content: [
|
|
579
|
-
{
|
|
580
|
-
type: 'text',
|
|
581
|
-
text: `assistantmail_get_usage failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
582
|
-
},
|
|
583
|
-
],
|
|
584
|
-
isError: true,
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
},
|
|
588
|
-
);
|
|
589
|
-
|
|
590
|
-
server.tool(
|
|
591
|
-
'assistantmail_list_recipients',
|
|
592
|
-
'Lists approved/pending recipients for the authenticated account.',
|
|
593
|
-
{
|
|
594
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
595
|
-
},
|
|
596
|
-
async ({ apiKey }) => {
|
|
597
|
-
try {
|
|
598
|
-
const result = await assistantMailGet('/v1/recipients', apiKey);
|
|
599
|
-
return {
|
|
600
|
-
content: [
|
|
601
|
-
{
|
|
602
|
-
type: 'text',
|
|
603
|
-
text: `Retrieved ${Array.isArray(result.data?.recipients) ? result.data.recipients.length : 0} recipient(s).`,
|
|
604
|
-
},
|
|
605
|
-
],
|
|
606
|
-
structuredContent: result,
|
|
607
|
-
};
|
|
608
|
-
} catch (error) {
|
|
609
|
-
return {
|
|
610
|
-
content: [
|
|
611
|
-
{
|
|
612
|
-
type: 'text',
|
|
613
|
-
text: `assistantmail_list_recipients failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
614
|
-
},
|
|
615
|
-
],
|
|
616
|
-
isError: true,
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
},
|
|
620
|
-
);
|
|
621
|
-
|
|
622
|
-
server.tool(
|
|
623
|
-
'assistantmail_add_recipient',
|
|
624
|
-
'Adds a recipient and sends a consent invitation when required.',
|
|
625
|
-
{
|
|
626
|
-
email: z.string().email(),
|
|
627
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
628
|
-
},
|
|
629
|
-
async ({ email, apiKey }) => {
|
|
630
|
-
try {
|
|
631
|
-
const result = await assistantMailPost('/v1/recipients', apiKey, { email });
|
|
632
|
-
return {
|
|
633
|
-
content: [
|
|
634
|
-
{
|
|
635
|
-
type: 'text',
|
|
636
|
-
text: `Processed recipient ${email}.`,
|
|
637
|
-
},
|
|
638
|
-
],
|
|
639
|
-
structuredContent: result,
|
|
640
|
-
};
|
|
641
|
-
} catch (error) {
|
|
642
|
-
return {
|
|
643
|
-
content: [
|
|
644
|
-
{
|
|
645
|
-
type: 'text',
|
|
646
|
-
text: `assistantmail_add_recipient failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
647
|
-
},
|
|
648
|
-
],
|
|
649
|
-
isError: true,
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
},
|
|
653
|
-
);
|
|
654
|
-
|
|
655
|
-
server.tool(
|
|
656
|
-
'assistantmail_remove_recipient',
|
|
657
|
-
'Removes a recipient from the allowed recipient list.',
|
|
658
|
-
{
|
|
659
|
-
email: z.string().email(),
|
|
660
|
-
apiKey: z.string().startsWith('amk_').optional(),
|
|
661
|
-
},
|
|
662
|
-
async ({ email, apiKey }) => {
|
|
663
|
-
try {
|
|
664
|
-
const result = await assistantMailDelete(`/v1/recipients/${encodeURIComponent(email)}`, apiKey);
|
|
665
|
-
return {
|
|
666
|
-
content: [
|
|
667
|
-
{
|
|
668
|
-
type: 'text',
|
|
669
|
-
text: `Removed recipient ${email}.`,
|
|
670
|
-
},
|
|
671
|
-
],
|
|
672
|
-
structuredContent: result,
|
|
673
|
-
};
|
|
674
|
-
} catch (error) {
|
|
675
|
-
return {
|
|
676
|
-
content: [
|
|
677
|
-
{
|
|
678
|
-
type: 'text',
|
|
679
|
-
text: `assistantmail_remove_recipient failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
680
|
-
},
|
|
681
|
-
],
|
|
682
|
-
isError: true,
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
},
|
|
686
|
-
);
|
|
687
|
-
|
|
688
|
-
server.tool(
|
|
689
|
-
'assistantmail_send_email_reference',
|
|
690
|
-
'Provides the AssistantMail REST endpoint and required headers for email send requests.',
|
|
691
|
-
{
|
|
692
|
-
mailboxId: z.string().min(1),
|
|
693
|
-
},
|
|
694
|
-
async ({ mailboxId }) => ({
|
|
695
|
-
content: [
|
|
696
|
-
{
|
|
697
|
-
type: 'text',
|
|
698
|
-
text: [
|
|
699
|
-
`Use POST ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages to queue outbound email.`,
|
|
700
|
-
'Headers: x-api-key: <your-api-key>, Content-Type: application/json',
|
|
701
|
-
'Body fields: to (required), subject (required), text and/or html (at least one required).',
|
|
702
|
-
].join(' '),
|
|
703
|
-
},
|
|
704
|
-
],
|
|
705
|
-
structuredContent: {
|
|
706
|
-
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages`,
|
|
707
|
-
method: 'POST',
|
|
708
|
-
headers: {
|
|
709
|
-
'x-api-key': '<your-api-key>',
|
|
710
|
-
'Content-Type': 'application/json',
|
|
711
|
-
},
|
|
712
|
-
bodyFields: {
|
|
713
|
-
to: 'string (required) - recipient email address',
|
|
714
|
-
subject: 'string (required)',
|
|
715
|
-
text: 'string (optional) - plain-text body',
|
|
716
|
-
html: 'string (optional) - HTML body',
|
|
717
|
-
},
|
|
718
|
-
note: 'At least one of text or html is required. Provide both for best email client compatibility.',
|
|
719
|
-
exampleBody: {
|
|
720
|
-
to: 'recipient@example.com',
|
|
721
|
-
subject: 'Hello from AssistantMail',
|
|
722
|
-
text: 'Message body',
|
|
723
|
-
html: '<p>Message body</p>',
|
|
724
|
-
},
|
|
725
|
-
},
|
|
726
|
-
}),
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
server.tool(
|
|
730
|
-
'assistantmail_list_messages_reference',
|
|
731
|
-
'Provides the AssistantMail REST endpoint for listing messages (inbound and outbound) in a mailbox.',
|
|
732
|
-
{
|
|
733
|
-
mailboxId: z.string().min(1),
|
|
734
|
-
},
|
|
735
|
-
async ({ mailboxId }) => ({
|
|
736
|
-
content: [
|
|
737
|
-
{
|
|
738
|
-
type: 'text',
|
|
739
|
-
text: [
|
|
740
|
-
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages to list messages.`,
|
|
741
|
-
'Headers: x-api-key: <your-api-key>.',
|
|
742
|
-
'Optional query params: since (ISO timestamp to filter messages after), limit (max 100, default 50).',
|
|
743
|
-
'Returns both inbound and outbound messages.',
|
|
744
|
-
].join(' '),
|
|
745
|
-
},
|
|
746
|
-
],
|
|
747
|
-
structuredContent: {
|
|
748
|
-
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages`,
|
|
749
|
-
method: 'GET',
|
|
750
|
-
headers: {
|
|
751
|
-
'x-api-key': '<your-api-key>',
|
|
752
|
-
},
|
|
753
|
-
queryParams: {
|
|
754
|
-
since: 'ISO 8601 timestamp (optional) - return messages after this time',
|
|
755
|
-
limit: 'integer 1-100 (optional, default 50)',
|
|
756
|
-
},
|
|
757
|
-
},
|
|
758
|
-
}),
|
|
759
|
-
);
|
|
760
|
-
|
|
761
|
-
server.tool(
|
|
762
|
-
'assistantmail_get_message_reference',
|
|
763
|
-
'Provides the AssistantMail REST endpoint for fetching a single message including its full body content.',
|
|
764
|
-
{
|
|
765
|
-
mailboxId: z.string().min(1),
|
|
766
|
-
messageId: z.string().min(1),
|
|
767
|
-
},
|
|
768
|
-
async ({ mailboxId, messageId }) => ({
|
|
769
|
-
content: [
|
|
770
|
-
{
|
|
771
|
-
type: 'text',
|
|
772
|
-
text: [
|
|
773
|
-
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages/${messageId} to fetch the full message.`,
|
|
774
|
-
'Headers: x-api-key: <your-api-key>.',
|
|
775
|
-
'Returns message metadata plus textBody and htmlBody fields populated from storage when still within the body-retention window.',
|
|
776
|
-
'If the retention window has elapsed, textBody/htmlBody are omitted and bodyExpired is true.',
|
|
777
|
-
].join(' '),
|
|
778
|
-
},
|
|
779
|
-
],
|
|
780
|
-
structuredContent: {
|
|
781
|
-
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages/${messageId}`,
|
|
782
|
-
method: 'GET',
|
|
783
|
-
headers: {
|
|
784
|
-
'x-api-key': '<your-api-key>',
|
|
785
|
-
},
|
|
786
|
-
responseFields: {
|
|
787
|
-
message: {
|
|
788
|
-
messageId: 'string',
|
|
789
|
-
direction: '"inbound" | "outbound"',
|
|
790
|
-
from: 'string',
|
|
791
|
-
to: 'string',
|
|
792
|
-
subject: 'string',
|
|
793
|
-
status: '"queued" | "sent" | "failed" | "received"',
|
|
794
|
-
textBody: 'string | undefined - plain-text body (populated at read time)',
|
|
795
|
-
htmlBody: 'string | undefined - HTML body (populated at read time)',
|
|
796
|
-
bodyExpired: 'boolean | undefined - true when body retention has elapsed',
|
|
797
|
-
createdAt: 'ISO timestamp',
|
|
798
|
-
},
|
|
799
|
-
},
|
|
800
|
-
},
|
|
801
|
-
}),
|
|
802
|
-
);
|
|
803
|
-
|
|
804
|
-
server.tool(
|
|
805
|
-
'assistantmail_get_usage_reference',
|
|
806
|
-
'Provides the AssistantMail REST endpoint for checking send quota usage for a mailbox.',
|
|
807
|
-
{
|
|
808
|
-
mailboxId: z.string().min(1),
|
|
809
|
-
},
|
|
810
|
-
async ({ mailboxId }) => ({
|
|
811
|
-
content: [
|
|
812
|
-
{
|
|
813
|
-
type: 'text',
|
|
814
|
-
text: [
|
|
815
|
-
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/usage to check send quota.`,
|
|
816
|
-
'Headers: x-api-key: <your-api-key>.',
|
|
817
|
-
'Returns daily and monthly usage counts and limits for the account tier.',
|
|
818
|
-
'A null limit means unlimited for that period.',
|
|
819
|
-
].join(' '),
|
|
820
|
-
},
|
|
821
|
-
],
|
|
822
|
-
structuredContent: {
|
|
823
|
-
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/usage`,
|
|
824
|
-
method: 'GET',
|
|
825
|
-
headers: {
|
|
826
|
-
'x-api-key': '<your-api-key>',
|
|
827
|
-
},
|
|
828
|
-
responseFields: {
|
|
829
|
-
tier: 'string - account plan name',
|
|
830
|
-
day: { used: 'number', limit: 'number | null' },
|
|
831
|
-
month: { used: 'number', limit: 'number | null' },
|
|
832
|
-
},
|
|
833
|
-
},
|
|
834
|
-
}),
|
|
835
|
-
);
|
|
836
|
-
|
|
837
|
-
const transport = new StdioServerTransport();
|
|
838
|
-
await server.connect(transport);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
const apiBaseUrl = process.env.ASSISTANT_MAIL_API_BASE_URL ?? 'https://api.assistant-mail.ai';
|
|
8
|
+
const defaultApiKey = process.env.ASSISTANT_MAIL_API_KEY ?? '';
|
|
9
|
+
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: 'assistantmail-mcp',
|
|
12
|
+
version: '1.2.0',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
function resolveApiKey(inputApiKey) {
|
|
16
|
+
const key = (inputApiKey ?? '').trim() || defaultApiKey.trim();
|
|
17
|
+
return key.startsWith('amk_') ? key : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function assistantMailRequest({ method, path, apiKey, query = {}, body }) {
|
|
21
|
+
const key = resolveApiKey(apiKey);
|
|
22
|
+
if (!key) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
'Missing API key. Provide apiKey in tool input or set ASSISTANT_MAIL_API_KEY in the MCP server environment.',
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const url = new URL(path, apiBaseUrl);
|
|
29
|
+
for (const [k, v] of Object.entries(query)) {
|
|
30
|
+
if (v !== undefined && v !== null && `${v}`.length > 0) {
|
|
31
|
+
url.searchParams.set(k, `${v}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const headers = {
|
|
36
|
+
Accept: 'application/json',
|
|
37
|
+
'x-api-key': key,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (body !== undefined) {
|
|
41
|
+
headers['Content-Type'] = 'application/json';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const res = await fetch(url, {
|
|
45
|
+
method,
|
|
46
|
+
headers,
|
|
47
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const raw = await res.text();
|
|
51
|
+
let parsed = null;
|
|
52
|
+
try {
|
|
53
|
+
parsed = raw ? JSON.parse(raw) : null;
|
|
54
|
+
} catch {
|
|
55
|
+
parsed = { raw };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!res.ok) {
|
|
59
|
+
const message = typeof parsed?.message === 'string'
|
|
60
|
+
? parsed.message
|
|
61
|
+
: `AssistantMail API request failed (${res.status}).`;
|
|
62
|
+
throw new Error(message);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
data: parsed,
|
|
67
|
+
status: res.status,
|
|
68
|
+
request: {
|
|
69
|
+
method,
|
|
70
|
+
url: url.toString(),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function assistantMailGet(path, apiKey, query = {}) {
|
|
76
|
+
return assistantMailRequest({ method: 'GET', path, apiKey, query });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function assistantMailPost(path, apiKey, body) {
|
|
80
|
+
return assistantMailRequest({ method: 'POST', path, apiKey, body });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function assistantMailPut(path, apiKey, body) {
|
|
84
|
+
return assistantMailRequest({ method: 'PUT', path, apiKey, body });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function assistantMailDelete(path, apiKey, body) {
|
|
88
|
+
return assistantMailRequest({ method: 'DELETE', path, apiKey, body });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
server.tool(
|
|
92
|
+
'assistantmail_health',
|
|
93
|
+
'Returns AssistantMail MCP server and API endpoint metadata.',
|
|
94
|
+
{},
|
|
95
|
+
async () => ({
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: 'text',
|
|
99
|
+
text: `assistantmail-mcp is running. API base URL: ${apiBaseUrl}`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
structuredContent: {
|
|
103
|
+
status: 'ok',
|
|
104
|
+
apiBaseUrl,
|
|
105
|
+
},
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
server.tool(
|
|
110
|
+
'assistantmail_get_me',
|
|
111
|
+
'Gets account profile and tier metadata for the authenticated account.',
|
|
112
|
+
{
|
|
113
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
114
|
+
},
|
|
115
|
+
async ({ apiKey }) => {
|
|
116
|
+
try {
|
|
117
|
+
const result = await assistantMailGet('/v1/users/me', apiKey);
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: 'text',
|
|
122
|
+
text: `Retrieved account profile for ${result.data?.email ?? 'authenticated account'}.`,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
structuredContent: result,
|
|
126
|
+
};
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: 'text',
|
|
132
|
+
text: `assistantmail_get_me failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
isError: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
server.tool(
|
|
142
|
+
'assistantmail_get_inbound_policy',
|
|
143
|
+
'Gets inbound email policy settings for the authenticated account.',
|
|
144
|
+
{
|
|
145
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
146
|
+
},
|
|
147
|
+
async ({ apiKey }) => {
|
|
148
|
+
try {
|
|
149
|
+
const result = await assistantMailGet('/v1/inbound-policy', apiKey);
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: 'text',
|
|
154
|
+
text: `Retrieved inbound policy (${result.data?.policy ?? 'unknown'}).`,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
structuredContent: result,
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return {
|
|
161
|
+
content: [
|
|
162
|
+
{
|
|
163
|
+
type: 'text',
|
|
164
|
+
text: `assistantmail_get_inbound_policy failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
isError: true,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
server.tool(
|
|
174
|
+
'assistantmail_update_inbound_policy',
|
|
175
|
+
'Updates inbound email policy for the authenticated account.',
|
|
176
|
+
{
|
|
177
|
+
policy: z.enum(['owner', 'list', 'sent']),
|
|
178
|
+
allowedSenders: z.array(z.string().email()).optional(),
|
|
179
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
180
|
+
},
|
|
181
|
+
async ({ policy, allowedSenders, apiKey }) => {
|
|
182
|
+
try {
|
|
183
|
+
const result = await assistantMailPut('/v1/inbound-policy', apiKey, {
|
|
184
|
+
policy,
|
|
185
|
+
allowedSenders,
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: 'text',
|
|
191
|
+
text: `Updated inbound policy to ${policy}.`,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
structuredContent: result,
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: 'text',
|
|
201
|
+
text: `assistantmail_update_inbound_policy failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
isError: true,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
server.tool(
|
|
211
|
+
'assistantmail_create_mailbox',
|
|
212
|
+
'Creates a mailbox for the authenticated account.',
|
|
213
|
+
{
|
|
214
|
+
displayName: z.string().min(1).max(120).optional(),
|
|
215
|
+
address: z.string().min(1).optional(),
|
|
216
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
217
|
+
},
|
|
218
|
+
async ({ displayName, address, apiKey }) => {
|
|
219
|
+
try {
|
|
220
|
+
const result = await assistantMailPost('/v1/mailboxes', apiKey, {
|
|
221
|
+
displayName,
|
|
222
|
+
address,
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
content: [
|
|
226
|
+
{
|
|
227
|
+
type: 'text',
|
|
228
|
+
text: `Created mailbox ${result.data?.mailbox?.mailboxId ?? '(unknown id)'}.`,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
structuredContent: result,
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return {
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
type: 'text',
|
|
238
|
+
text: `assistantmail_create_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
isError: true,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
server.tool(
|
|
248
|
+
'assistantmail_list_mailboxes',
|
|
249
|
+
'Lists mailboxes accessible to the authenticated account.',
|
|
250
|
+
{
|
|
251
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
252
|
+
},
|
|
253
|
+
async ({ apiKey }) => {
|
|
254
|
+
try {
|
|
255
|
+
const result = await assistantMailGet('/v1/mailboxes', apiKey);
|
|
256
|
+
return {
|
|
257
|
+
content: [
|
|
258
|
+
{
|
|
259
|
+
type: 'text',
|
|
260
|
+
text: `Retrieved ${Array.isArray(result.data?.mailboxes) ? result.data.mailboxes.length : 0} mailbox(es).`,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
structuredContent: result,
|
|
264
|
+
};
|
|
265
|
+
} catch (error) {
|
|
266
|
+
return {
|
|
267
|
+
content: [
|
|
268
|
+
{
|
|
269
|
+
type: 'text',
|
|
270
|
+
text: `assistantmail_list_mailboxes failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
isError: true,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
server.tool(
|
|
280
|
+
'assistantmail_get_mailbox',
|
|
281
|
+
'Gets mailbox metadata for a mailbox ID.',
|
|
282
|
+
{
|
|
283
|
+
mailboxId: z.string().min(1),
|
|
284
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
285
|
+
},
|
|
286
|
+
async ({ mailboxId, apiKey }) => {
|
|
287
|
+
try {
|
|
288
|
+
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}`, apiKey);
|
|
289
|
+
return {
|
|
290
|
+
content: [
|
|
291
|
+
{
|
|
292
|
+
type: 'text',
|
|
293
|
+
text: `Retrieved mailbox ${mailboxId}.`,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
structuredContent: result,
|
|
297
|
+
};
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
content: [
|
|
301
|
+
{
|
|
302
|
+
type: 'text',
|
|
303
|
+
text: `assistantmail_get_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
isError: true,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
server.tool(
|
|
313
|
+
'assistantmail_update_mailbox',
|
|
314
|
+
'Updates mailbox metadata (currently display name).',
|
|
315
|
+
{
|
|
316
|
+
mailboxId: z.string().min(1),
|
|
317
|
+
displayName: z.string().min(1).max(120),
|
|
318
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
319
|
+
},
|
|
320
|
+
async ({ mailboxId, displayName, apiKey }) => {
|
|
321
|
+
try {
|
|
322
|
+
const result = await assistantMailPut(`/v1/mailboxes/${mailboxId}`, apiKey, {
|
|
323
|
+
displayName,
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
content: [
|
|
327
|
+
{
|
|
328
|
+
type: 'text',
|
|
329
|
+
text: `Updated mailbox ${mailboxId}.`,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
structuredContent: result,
|
|
333
|
+
};
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return {
|
|
336
|
+
content: [
|
|
337
|
+
{
|
|
338
|
+
type: 'text',
|
|
339
|
+
text: `assistantmail_update_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
isError: true,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
server.tool(
|
|
349
|
+
'assistantmail_delete_mailbox',
|
|
350
|
+
'Deletes a mailbox and all associated messages.',
|
|
351
|
+
{
|
|
352
|
+
mailboxId: z.string().min(1),
|
|
353
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
354
|
+
},
|
|
355
|
+
async ({ mailboxId, apiKey }) => {
|
|
356
|
+
try {
|
|
357
|
+
const result = await assistantMailDelete(`/v1/mailboxes/${mailboxId}`, apiKey);
|
|
358
|
+
return {
|
|
359
|
+
content: [
|
|
360
|
+
{
|
|
361
|
+
type: 'text',
|
|
362
|
+
text: `Deleted mailbox ${mailboxId}.`,
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
structuredContent: result,
|
|
366
|
+
};
|
|
367
|
+
} catch (error) {
|
|
368
|
+
return {
|
|
369
|
+
content: [
|
|
370
|
+
{
|
|
371
|
+
type: 'text',
|
|
372
|
+
text: `assistantmail_delete_mailbox failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
isError: true,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
server.tool(
|
|
382
|
+
'assistantmail_list_messages',
|
|
383
|
+
'Lists inbound and outbound messages for a mailbox.',
|
|
384
|
+
{
|
|
385
|
+
mailboxId: z.string().min(1),
|
|
386
|
+
since: z.string().datetime().optional(),
|
|
387
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
388
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
389
|
+
},
|
|
390
|
+
async ({ mailboxId, since, limit, apiKey }) => {
|
|
391
|
+
try {
|
|
392
|
+
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
393
|
+
since,
|
|
394
|
+
limit,
|
|
395
|
+
});
|
|
396
|
+
return {
|
|
397
|
+
content: [
|
|
398
|
+
{
|
|
399
|
+
type: 'text',
|
|
400
|
+
text: `Retrieved ${Array.isArray(result.data?.messages) ? result.data.messages.length : 0} message(s) for mailbox ${mailboxId}.`,
|
|
401
|
+
},
|
|
402
|
+
],
|
|
403
|
+
structuredContent: result,
|
|
404
|
+
};
|
|
405
|
+
} catch (error) {
|
|
406
|
+
return {
|
|
407
|
+
content: [
|
|
408
|
+
{
|
|
409
|
+
type: 'text',
|
|
410
|
+
text: `assistantmail_list_messages failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
isError: true,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
server.tool(
|
|
420
|
+
'assistantmail_get_message',
|
|
421
|
+
'Gets a specific message for a mailbox, including hydrated text/html bodies when available.',
|
|
422
|
+
{
|
|
423
|
+
mailboxId: z.string().min(1),
|
|
424
|
+
messageId: z.string().min(1),
|
|
425
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
426
|
+
},
|
|
427
|
+
async ({ mailboxId, messageId, apiKey }) => {
|
|
428
|
+
try {
|
|
429
|
+
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/messages/${messageId}`, apiKey);
|
|
430
|
+
return {
|
|
431
|
+
content: [
|
|
432
|
+
{
|
|
433
|
+
type: 'text',
|
|
434
|
+
text: `Retrieved message ${messageId} for mailbox ${mailboxId}.`,
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
structuredContent: result,
|
|
438
|
+
};
|
|
439
|
+
} catch (error) {
|
|
440
|
+
return {
|
|
441
|
+
content: [
|
|
442
|
+
{
|
|
443
|
+
type: 'text',
|
|
444
|
+
text: `assistantmail_get_message failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
isError: true,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
server.tool(
|
|
454
|
+
'assistantmail_send_email',
|
|
455
|
+
'Queues an outbound email for a mailbox.',
|
|
456
|
+
{
|
|
457
|
+
mailboxId: z.string().min(1),
|
|
458
|
+
to: z.string().email(),
|
|
459
|
+
subject: z.string().min(1),
|
|
460
|
+
text: z.string().optional(),
|
|
461
|
+
html: z.string().optional(),
|
|
462
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
463
|
+
},
|
|
464
|
+
async ({ mailboxId, to, subject, text, html, apiKey }) => {
|
|
465
|
+
if (!text && !html) {
|
|
466
|
+
return {
|
|
467
|
+
content: [
|
|
468
|
+
{
|
|
469
|
+
type: 'text',
|
|
470
|
+
text: 'assistantmail_send_email failed: Provide at least one of text or html.',
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
isError: true,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
const result = await assistantMailPost(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
479
|
+
to,
|
|
480
|
+
subject,
|
|
481
|
+
text,
|
|
482
|
+
html,
|
|
483
|
+
});
|
|
484
|
+
return {
|
|
485
|
+
content: [
|
|
486
|
+
{
|
|
487
|
+
type: 'text',
|
|
488
|
+
text: `Queued email to ${to} from mailbox ${mailboxId}.`,
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
structuredContent: result,
|
|
492
|
+
};
|
|
493
|
+
} catch (error) {
|
|
494
|
+
return {
|
|
495
|
+
content: [
|
|
496
|
+
{
|
|
497
|
+
type: 'text',
|
|
498
|
+
text: `assistantmail_send_email failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
isError: true,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
server.tool(
|
|
508
|
+
'assistantmail_delete_messages',
|
|
509
|
+
'Deletes messages from a mailbox by IDs or deletes all messages.',
|
|
510
|
+
{
|
|
511
|
+
mailboxId: z.string().min(1),
|
|
512
|
+
messageIds: z.array(z.string().min(1)).optional(),
|
|
513
|
+
deleteAll: z.boolean().optional(),
|
|
514
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
515
|
+
},
|
|
516
|
+
async ({ mailboxId, messageIds, deleteAll, apiKey }) => {
|
|
517
|
+
if (!deleteAll && (!Array.isArray(messageIds) || messageIds.length === 0)) {
|
|
518
|
+
return {
|
|
519
|
+
content: [
|
|
520
|
+
{
|
|
521
|
+
type: 'text',
|
|
522
|
+
text: 'assistantmail_delete_messages failed: Provide messageIds or set deleteAll to true.',
|
|
523
|
+
},
|
|
524
|
+
],
|
|
525
|
+
isError: true,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const result = await assistantMailDelete(`/v1/mailboxes/${mailboxId}/messages`, apiKey, {
|
|
531
|
+
messageIds,
|
|
532
|
+
deleteAll,
|
|
533
|
+
});
|
|
534
|
+
return {
|
|
535
|
+
content: [
|
|
536
|
+
{
|
|
537
|
+
type: 'text',
|
|
538
|
+
text: `Deleted messages for mailbox ${mailboxId}.`,
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
structuredContent: result,
|
|
542
|
+
};
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return {
|
|
545
|
+
content: [
|
|
546
|
+
{
|
|
547
|
+
type: 'text',
|
|
548
|
+
text: `assistantmail_delete_messages failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
isError: true,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
server.tool(
|
|
558
|
+
'assistantmail_get_usage',
|
|
559
|
+
'Gets daily and monthly send quota usage for a mailbox.',
|
|
560
|
+
{
|
|
561
|
+
mailboxId: z.string().min(1),
|
|
562
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
563
|
+
},
|
|
564
|
+
async ({ mailboxId, apiKey }) => {
|
|
565
|
+
try {
|
|
566
|
+
const result = await assistantMailGet(`/v1/mailboxes/${mailboxId}/usage`, apiKey);
|
|
567
|
+
return {
|
|
568
|
+
content: [
|
|
569
|
+
{
|
|
570
|
+
type: 'text',
|
|
571
|
+
text: `Retrieved usage for mailbox ${mailboxId}.`,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
structuredContent: result,
|
|
575
|
+
};
|
|
576
|
+
} catch (error) {
|
|
577
|
+
return {
|
|
578
|
+
content: [
|
|
579
|
+
{
|
|
580
|
+
type: 'text',
|
|
581
|
+
text: `assistantmail_get_usage failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
582
|
+
},
|
|
583
|
+
],
|
|
584
|
+
isError: true,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
server.tool(
|
|
591
|
+
'assistantmail_list_recipients',
|
|
592
|
+
'Lists approved/pending recipients for the authenticated account.',
|
|
593
|
+
{
|
|
594
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
595
|
+
},
|
|
596
|
+
async ({ apiKey }) => {
|
|
597
|
+
try {
|
|
598
|
+
const result = await assistantMailGet('/v1/recipients', apiKey);
|
|
599
|
+
return {
|
|
600
|
+
content: [
|
|
601
|
+
{
|
|
602
|
+
type: 'text',
|
|
603
|
+
text: `Retrieved ${Array.isArray(result.data?.recipients) ? result.data.recipients.length : 0} recipient(s).`,
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
structuredContent: result,
|
|
607
|
+
};
|
|
608
|
+
} catch (error) {
|
|
609
|
+
return {
|
|
610
|
+
content: [
|
|
611
|
+
{
|
|
612
|
+
type: 'text',
|
|
613
|
+
text: `assistantmail_list_recipients failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
614
|
+
},
|
|
615
|
+
],
|
|
616
|
+
isError: true,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
server.tool(
|
|
623
|
+
'assistantmail_add_recipient',
|
|
624
|
+
'Adds a recipient and sends a consent invitation when required.',
|
|
625
|
+
{
|
|
626
|
+
email: z.string().email(),
|
|
627
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
628
|
+
},
|
|
629
|
+
async ({ email, apiKey }) => {
|
|
630
|
+
try {
|
|
631
|
+
const result = await assistantMailPost('/v1/recipients', apiKey, { email });
|
|
632
|
+
return {
|
|
633
|
+
content: [
|
|
634
|
+
{
|
|
635
|
+
type: 'text',
|
|
636
|
+
text: `Processed recipient ${email}.`,
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
structuredContent: result,
|
|
640
|
+
};
|
|
641
|
+
} catch (error) {
|
|
642
|
+
return {
|
|
643
|
+
content: [
|
|
644
|
+
{
|
|
645
|
+
type: 'text',
|
|
646
|
+
text: `assistantmail_add_recipient failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
647
|
+
},
|
|
648
|
+
],
|
|
649
|
+
isError: true,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
},
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
server.tool(
|
|
656
|
+
'assistantmail_remove_recipient',
|
|
657
|
+
'Removes a recipient from the allowed recipient list.',
|
|
658
|
+
{
|
|
659
|
+
email: z.string().email(),
|
|
660
|
+
apiKey: z.string().startsWith('amk_').optional(),
|
|
661
|
+
},
|
|
662
|
+
async ({ email, apiKey }) => {
|
|
663
|
+
try {
|
|
664
|
+
const result = await assistantMailDelete(`/v1/recipients/${encodeURIComponent(email)}`, apiKey);
|
|
665
|
+
return {
|
|
666
|
+
content: [
|
|
667
|
+
{
|
|
668
|
+
type: 'text',
|
|
669
|
+
text: `Removed recipient ${email}.`,
|
|
670
|
+
},
|
|
671
|
+
],
|
|
672
|
+
structuredContent: result,
|
|
673
|
+
};
|
|
674
|
+
} catch (error) {
|
|
675
|
+
return {
|
|
676
|
+
content: [
|
|
677
|
+
{
|
|
678
|
+
type: 'text',
|
|
679
|
+
text: `assistantmail_remove_recipient failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
680
|
+
},
|
|
681
|
+
],
|
|
682
|
+
isError: true,
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
server.tool(
|
|
689
|
+
'assistantmail_send_email_reference',
|
|
690
|
+
'Provides the AssistantMail REST endpoint and required headers for email send requests.',
|
|
691
|
+
{
|
|
692
|
+
mailboxId: z.string().min(1),
|
|
693
|
+
},
|
|
694
|
+
async ({ mailboxId }) => ({
|
|
695
|
+
content: [
|
|
696
|
+
{
|
|
697
|
+
type: 'text',
|
|
698
|
+
text: [
|
|
699
|
+
`Use POST ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages to queue outbound email.`,
|
|
700
|
+
'Headers: x-api-key: <your-api-key>, Content-Type: application/json',
|
|
701
|
+
'Body fields: to (required), subject (required), text and/or html (at least one required).',
|
|
702
|
+
].join(' '),
|
|
703
|
+
},
|
|
704
|
+
],
|
|
705
|
+
structuredContent: {
|
|
706
|
+
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages`,
|
|
707
|
+
method: 'POST',
|
|
708
|
+
headers: {
|
|
709
|
+
'x-api-key': '<your-api-key>',
|
|
710
|
+
'Content-Type': 'application/json',
|
|
711
|
+
},
|
|
712
|
+
bodyFields: {
|
|
713
|
+
to: 'string (required) - recipient email address',
|
|
714
|
+
subject: 'string (required)',
|
|
715
|
+
text: 'string (optional) - plain-text body',
|
|
716
|
+
html: 'string (optional) - HTML body',
|
|
717
|
+
},
|
|
718
|
+
note: 'At least one of text or html is required. Provide both for best email client compatibility.',
|
|
719
|
+
exampleBody: {
|
|
720
|
+
to: 'recipient@example.com',
|
|
721
|
+
subject: 'Hello from AssistantMail',
|
|
722
|
+
text: 'Message body',
|
|
723
|
+
html: '<p>Message body</p>',
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
}),
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
server.tool(
|
|
730
|
+
'assistantmail_list_messages_reference',
|
|
731
|
+
'Provides the AssistantMail REST endpoint for listing messages (inbound and outbound) in a mailbox.',
|
|
732
|
+
{
|
|
733
|
+
mailboxId: z.string().min(1),
|
|
734
|
+
},
|
|
735
|
+
async ({ mailboxId }) => ({
|
|
736
|
+
content: [
|
|
737
|
+
{
|
|
738
|
+
type: 'text',
|
|
739
|
+
text: [
|
|
740
|
+
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages to list messages.`,
|
|
741
|
+
'Headers: x-api-key: <your-api-key>.',
|
|
742
|
+
'Optional query params: since (ISO timestamp to filter messages after), limit (max 100, default 50).',
|
|
743
|
+
'Returns both inbound and outbound messages.',
|
|
744
|
+
].join(' '),
|
|
745
|
+
},
|
|
746
|
+
],
|
|
747
|
+
structuredContent: {
|
|
748
|
+
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages`,
|
|
749
|
+
method: 'GET',
|
|
750
|
+
headers: {
|
|
751
|
+
'x-api-key': '<your-api-key>',
|
|
752
|
+
},
|
|
753
|
+
queryParams: {
|
|
754
|
+
since: 'ISO 8601 timestamp (optional) - return messages after this time',
|
|
755
|
+
limit: 'integer 1-100 (optional, default 50)',
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
}),
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
server.tool(
|
|
762
|
+
'assistantmail_get_message_reference',
|
|
763
|
+
'Provides the AssistantMail REST endpoint for fetching a single message including its full body content.',
|
|
764
|
+
{
|
|
765
|
+
mailboxId: z.string().min(1),
|
|
766
|
+
messageId: z.string().min(1),
|
|
767
|
+
},
|
|
768
|
+
async ({ mailboxId, messageId }) => ({
|
|
769
|
+
content: [
|
|
770
|
+
{
|
|
771
|
+
type: 'text',
|
|
772
|
+
text: [
|
|
773
|
+
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages/${messageId} to fetch the full message.`,
|
|
774
|
+
'Headers: x-api-key: <your-api-key>.',
|
|
775
|
+
'Returns message metadata plus textBody and htmlBody fields populated from storage when still within the body-retention window.',
|
|
776
|
+
'If the retention window has elapsed, textBody/htmlBody are omitted and bodyExpired is true.',
|
|
777
|
+
].join(' '),
|
|
778
|
+
},
|
|
779
|
+
],
|
|
780
|
+
structuredContent: {
|
|
781
|
+
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/messages/${messageId}`,
|
|
782
|
+
method: 'GET',
|
|
783
|
+
headers: {
|
|
784
|
+
'x-api-key': '<your-api-key>',
|
|
785
|
+
},
|
|
786
|
+
responseFields: {
|
|
787
|
+
message: {
|
|
788
|
+
messageId: 'string',
|
|
789
|
+
direction: '"inbound" | "outbound"',
|
|
790
|
+
from: 'string',
|
|
791
|
+
to: 'string',
|
|
792
|
+
subject: 'string',
|
|
793
|
+
status: '"queued" | "sent" | "failed" | "received"',
|
|
794
|
+
textBody: 'string | undefined - plain-text body (populated at read time)',
|
|
795
|
+
htmlBody: 'string | undefined - HTML body (populated at read time)',
|
|
796
|
+
bodyExpired: 'boolean | undefined - true when body retention has elapsed',
|
|
797
|
+
createdAt: 'ISO timestamp',
|
|
798
|
+
},
|
|
799
|
+
},
|
|
800
|
+
},
|
|
801
|
+
}),
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
server.tool(
|
|
805
|
+
'assistantmail_get_usage_reference',
|
|
806
|
+
'Provides the AssistantMail REST endpoint for checking send quota usage for a mailbox.',
|
|
807
|
+
{
|
|
808
|
+
mailboxId: z.string().min(1),
|
|
809
|
+
},
|
|
810
|
+
async ({ mailboxId }) => ({
|
|
811
|
+
content: [
|
|
812
|
+
{
|
|
813
|
+
type: 'text',
|
|
814
|
+
text: [
|
|
815
|
+
`Use GET ${apiBaseUrl}/v1/mailboxes/${mailboxId}/usage to check send quota.`,
|
|
816
|
+
'Headers: x-api-key: <your-api-key>.',
|
|
817
|
+
'Returns daily and monthly usage counts and limits for the account tier.',
|
|
818
|
+
'A null limit means unlimited for that period.',
|
|
819
|
+
].join(' '),
|
|
820
|
+
},
|
|
821
|
+
],
|
|
822
|
+
structuredContent: {
|
|
823
|
+
endpoint: `${apiBaseUrl}/v1/mailboxes/${mailboxId}/usage`,
|
|
824
|
+
method: 'GET',
|
|
825
|
+
headers: {
|
|
826
|
+
'x-api-key': '<your-api-key>',
|
|
827
|
+
},
|
|
828
|
+
responseFields: {
|
|
829
|
+
tier: 'string - account plan name',
|
|
830
|
+
day: { used: 'number', limit: 'number | null' },
|
|
831
|
+
month: { used: 'number', limit: 'number | null' },
|
|
832
|
+
},
|
|
833
|
+
},
|
|
834
|
+
}),
|
|
835
|
+
);
|
|
836
|
+
|
|
837
|
+
const transport = new StdioServerTransport();
|
|
838
|
+
await server.connect(transport);
|