@neschadin/sendgrid-mcp 0.0.0-stage → 3.0.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.
@@ -0,0 +1,488 @@
1
+ import { z } from 'zod';
2
+ import type { ResponseFormat } from './tool_utils';
3
+
4
+ export function jsonText(data: unknown): string {
5
+ return JSON.stringify(data, null, 2);
6
+ }
7
+
8
+ export function buildPaginationMeta(params: {
9
+ totalCount: number;
10
+ count: number;
11
+ offset?: number;
12
+ }) {
13
+ const offset = params.offset ?? 0;
14
+ const hasMore = offset + params.count < params.totalCount;
15
+ return {
16
+ total_count: params.totalCount,
17
+ count: params.count,
18
+ offset,
19
+ has_more: hasMore,
20
+ next_offset: hasMore ? offset + params.count : null,
21
+ };
22
+ }
23
+
24
+ export function paginateArray<T>(items: T[], limit = 50, offset = 0) {
25
+ const safeLimit = Math.max(1, Math.min(limit, 1000));
26
+ const safeOffset = Math.max(0, offset);
27
+ const page = items.slice(safeOffset, safeOffset + safeLimit);
28
+ return {
29
+ items: page,
30
+ pagination: buildPaginationMeta({
31
+ totalCount: items.length,
32
+ count: page.length,
33
+ offset: safeOffset,
34
+ }),
35
+ };
36
+ }
37
+
38
+ export function formatReadResponse<T extends Record<string, unknown> | object>(
39
+ structuredContent: T,
40
+ markdownText: string,
41
+ responseFormat: ResponseFormat = 'markdown',
42
+ ) {
43
+ return {
44
+ structuredContent,
45
+ content: [
46
+ {
47
+ type: 'text' as const,
48
+ text:
49
+ responseFormat === 'json'
50
+ ? jsonText(structuredContent)
51
+ : markdownText,
52
+ },
53
+ ],
54
+ };
55
+ }
56
+
57
+ export function jsonReadResult<T extends Record<string, unknown> | object>(
58
+ structuredContent: T,
59
+ markdownText?: string,
60
+ responseFormat: ResponseFormat = 'markdown',
61
+ ) {
62
+ return formatReadResponse(
63
+ structuredContent,
64
+ markdownText ?? jsonText(structuredContent),
65
+ responseFormat,
66
+ );
67
+ }
68
+
69
+ export const PaginationMetaSchema = z.object({
70
+ total_count: z.number().int().nonnegative(),
71
+ count: z.number().int().nonnegative(),
72
+ offset: z.number().int().nonnegative(),
73
+ has_more: z.boolean(),
74
+ next_offset: z.number().int().nonnegative().nullable(),
75
+ });
76
+
77
+ export const PassthroughRecordSchema = z.record(z.string(), z.unknown());
78
+
79
+ export const PreflightOutputSchema = z.object({
80
+ ok: z.boolean(),
81
+ blockers: z.array(
82
+ z.object({
83
+ severity: z.literal('blocker'),
84
+ code: z.string(),
85
+ message: z.string(),
86
+ }),
87
+ ),
88
+ warnings: z.array(
89
+ z.object({
90
+ severity: z.literal('warning'),
91
+ code: z.string(),
92
+ message: z.string(),
93
+ }),
94
+ ),
95
+ info: z.array(
96
+ z.object({
97
+ severity: z.literal('info'),
98
+ code: z.string(),
99
+ message: z.string(),
100
+ }),
101
+ ),
102
+ });
103
+
104
+ export type PreflightOutput = z.infer<typeof PreflightOutputSchema>;
105
+
106
+ export const SendWithPreflightOutputSchema = z.object({
107
+ sent: z.boolean(),
108
+ report: PreflightOutputSchema,
109
+ statusCode: z.number().nullable(),
110
+ messageId: z.string().nullable(),
111
+ });
112
+
113
+ export const SendTestEmailOutputSchema = z.object({
114
+ sent: z.boolean(),
115
+ sandbox: z.boolean(),
116
+ report: PreflightOutputSchema,
117
+ statusCode: z.number().nullable(),
118
+ messageId: z.string().nullable(),
119
+ });
120
+
121
+ export const BatchIdOutputSchema = z.object({
122
+ batchId: z.string(),
123
+ });
124
+
125
+ export const ScheduledSendSchema = z.object({
126
+ batch_id: z.string(),
127
+ status: z.enum(['pause', 'cancel']),
128
+ });
129
+
130
+ export const ScheduledSendListOutputSchema = z.object({
131
+ count: z.number().int().nonnegative(),
132
+ sends: z.array(ScheduledSendSchema),
133
+ });
134
+
135
+ export const AsmGroupSchema = z
136
+ .object({
137
+ id: z.number().int(),
138
+ name: z.string(),
139
+ description: z.string().optional(),
140
+ is_default: z.boolean().optional(),
141
+ unsubscribes: z.number().int().optional(),
142
+ })
143
+ .passthrough();
144
+
145
+ export const AsmGroupListOutputSchema = z.object({
146
+ count: z.number().int().nonnegative(),
147
+ groups: z.array(AsmGroupSchema),
148
+ });
149
+
150
+ export const CategoryListOutputSchema = z.object({
151
+ count: z.number().int().nonnegative(),
152
+ categories: z.array(z.string()),
153
+ });
154
+
155
+ export const EnforcedTlsOutputSchema = z.object({
156
+ require_tls: z.boolean(),
157
+ require_valid_cert: z.boolean(),
158
+ version: z.number(),
159
+ });
160
+
161
+ export const ClassifyErrorOutputSchema = z.object({
162
+ category: z.string(),
163
+ statusCode: z.number().nullable(),
164
+ probableCauses: z.array(z.string()),
165
+ actions: z.array(z.string()),
166
+ });
167
+
168
+ export const TriageDeliveryOutputSchema = z.object({
169
+ scenario: z.string(),
170
+ findings: z.array(z.string()),
171
+ actions: z.array(z.string()),
172
+ });
173
+
174
+ export const AnalyzeEngagementOutputSchema = z.object({
175
+ totals: z.object({
176
+ events: z.number().int().nonnegative(),
177
+ opens: z.number().int().nonnegative(),
178
+ clicks: z.number().int().nonnegative(),
179
+ delivered: z.number().int().nonnegative(),
180
+ uniqueOpenApprox: z.number().int().nonnegative(),
181
+ }),
182
+ findings: z.array(z.string()),
183
+ suggestions: z.array(z.string()),
184
+ });
185
+
186
+ export const SyncTemplateIdsOutputSchema = z.object({
187
+ filePath: z.string(),
188
+ okCount: z.number().int().nonnegative(),
189
+ placeholderCount: z.number().int().nonnegative(),
190
+ missingCount: z.number().int().nonnegative(),
191
+ rows: z.array(z.string()),
192
+ unreferencedTemplateIds: z.array(z.string()),
193
+ });
194
+
195
+ export const UserAccountOutputSchema = PassthroughRecordSchema;
196
+ export const UserProfileOutputSchema = PassthroughRecordSchema;
197
+ export const UserCreditsOutputSchema = PassthroughRecordSchema;
198
+ export const SettingDetailOutputSchema = PassthroughRecordSchema;
199
+
200
+ export const VerifiedSenderSchema = z
201
+ .object({
202
+ id: z.number(),
203
+ nickname: z.string().optional(),
204
+ from_email: z.string().optional(),
205
+ from_name: z.string().optional(),
206
+ reply_to: z.string().optional(),
207
+ verified: z.boolean().optional(),
208
+ locked: z.boolean().optional(),
209
+ })
210
+ .passthrough();
211
+
212
+ export const VerifiedSendersListOutputSchema = PaginationMetaSchema.extend({
213
+ senders: z.array(VerifiedSenderSchema),
214
+ });
215
+
216
+ export const AuthenticatedDomainSchema = z
217
+ .object({
218
+ id: z.number(),
219
+ subdomain: z.string(),
220
+ domain: z.string(),
221
+ default: z.boolean().optional(),
222
+ valid: z.boolean().optional(),
223
+ legacy: z.boolean().optional(),
224
+ custom_spf: z.boolean().optional(),
225
+ automatic_security: z.boolean().optional(),
226
+ })
227
+ .passthrough();
228
+
229
+ export const AuthenticatedDomainsListOutputSchema = PaginationMetaSchema.extend({
230
+ domains: z.array(AuthenticatedDomainSchema),
231
+ });
232
+
233
+ export const BrandedLinkSchema = z
234
+ .object({
235
+ id: z.number(),
236
+ domain: z.string(),
237
+ subdomain: z.string(),
238
+ default: z.boolean().optional(),
239
+ valid: z.boolean().optional(),
240
+ legacy: z.boolean().optional(),
241
+ })
242
+ .passthrough();
243
+
244
+ export const BrandedLinksListOutputSchema = PaginationMetaSchema.extend({
245
+ links: z.array(BrandedLinkSchema),
246
+ });
247
+
248
+ export const SendGridAlertSchema = z
249
+ .object({
250
+ id: z.number(),
251
+ type: z.string(),
252
+ email_to: z.string().optional(),
253
+ frequency: z.string().optional(),
254
+ percentage: z.number().optional(),
255
+ created_at: z.number().optional(),
256
+ updated_at: z.number().optional(),
257
+ })
258
+ .passthrough();
259
+
260
+ export const AlertsListOutputSchema = PaginationMetaSchema.extend({
261
+ alerts: z.array(SendGridAlertSchema),
262
+ });
263
+
264
+ export const MailSettingSummarySchema = z
265
+ .object({
266
+ title: z.string().optional(),
267
+ name: z.string().optional(),
268
+ enabled: z.boolean().optional(),
269
+ })
270
+ .passthrough();
271
+
272
+ export const MailSettingsListOutputSchema = PaginationMetaSchema.extend({
273
+ settings: z.array(MailSettingSummarySchema),
274
+ });
275
+
276
+ export const TrackingSettingSummarySchema = z
277
+ .object({
278
+ name: z.string().optional(),
279
+ title: z.string().optional(),
280
+ description: z.string().optional(),
281
+ enabled: z.boolean().optional(),
282
+ })
283
+ .passthrough();
284
+
285
+ export const TrackingSettingsListOutputSchema = PaginationMetaSchema.extend({
286
+ settings: z.array(TrackingSettingSummarySchema),
287
+ });
288
+
289
+ export const InboundParseSettingSchema = z
290
+ .object({
291
+ url: z.string(),
292
+ hostname: z.string(),
293
+ spam_check: z.boolean().optional(),
294
+ send_raw: z.boolean().optional(),
295
+ })
296
+ .passthrough();
297
+
298
+ export const InboundParseSettingsListOutputSchema = PaginationMetaSchema.extend({
299
+ settings: z.array(InboundParseSettingSchema),
300
+ });
301
+
302
+ export const TemplateListItemSchema = z.object({
303
+ id: z.string(),
304
+ name: z.string(),
305
+ versions: z.number().int().nonnegative(),
306
+ activeSubject: z.string().nullable(),
307
+ updatedAt: z.string(),
308
+ });
309
+
310
+ export const ListTemplatesOutputSchema = PaginationMetaSchema.extend({
311
+ templates: z.array(TemplateListItemSchema),
312
+ });
313
+
314
+ export const TemplateHtmlOutputSchema = z.object({
315
+ templateId: z.string(),
316
+ versionId: z.string(),
317
+ active: z.boolean(),
318
+ name: z.string(),
319
+ subject: z.string(),
320
+ updatedAt: z.string(),
321
+ htmlContent: z.string(),
322
+ });
323
+
324
+ export const MessageActivitySchema = z
325
+ .object({
326
+ msg_id: z.string(),
327
+ from_email: z.string().optional(),
328
+ to_email: z.string().optional(),
329
+ subject: z.string().optional(),
330
+ status: z.string().optional(),
331
+ opens_count: z.number().optional(),
332
+ clicks_count: z.number().optional(),
333
+ last_event_time: z.string().optional(),
334
+ last_timestamp: z.number().optional(),
335
+ })
336
+ .passthrough();
337
+
338
+ export const SearchMessageActivityOutputSchema = PaginationMetaSchema.extend({
339
+ messages: z.array(MessageActivitySchema),
340
+ source: z.enum(['activity', 'logs']).optional(),
341
+ note: z.string().optional(),
342
+ });
343
+
344
+ export const EventWebhookSchema = z
345
+ .object({
346
+ id: z.string(),
347
+ enabled: z.boolean().optional(),
348
+ url: z.string().optional(),
349
+ delivered: z.boolean().optional(),
350
+ bounce: z.boolean().optional(),
351
+ deferred: z.boolean().optional(),
352
+ dropped: z.boolean().optional(),
353
+ open: z.boolean().optional(),
354
+ click: z.boolean().optional(),
355
+ public_key: z.string().optional(),
356
+ friendly_name: z.string().nullable().optional(),
357
+ created_date: z.string().nullable().optional(),
358
+ updated_date: z.string().optional(),
359
+ })
360
+ .passthrough();
361
+
362
+ export const EventWebhookListOutputSchema = PaginationMetaSchema.extend({
363
+ maxAllowed: z.number().nullable(),
364
+ webhooks: z.array(EventWebhookSchema),
365
+ });
366
+
367
+ export const WebhookReceiverStatusOutputSchema = z.object({
368
+ enabled: z.boolean(),
369
+ listening: z.boolean(),
370
+ host: z.string().optional(),
371
+ port: z.number().optional(),
372
+ path: z.string().optional(),
373
+ maxStoredEvents: z.number().int().nonnegative(),
374
+ storedEvents: z.number().int().nonnegative(),
375
+ eventTypeBreakdown: z.record(z.string(), z.number()),
376
+ signatureRequired: z.boolean(),
377
+ signatureConfigured: z.boolean(),
378
+ lastReceivedAt: z.string().optional(),
379
+ lastError: z.string().optional(),
380
+ });
381
+
382
+ export const ReceivedWebhookEventSchema = z.object({
383
+ receivedAt: z.string(),
384
+ signatureVerified: z.boolean(),
385
+ payload: PassthroughRecordSchema,
386
+ });
387
+
388
+ export const ReceivedWebhookEventsOutputSchema = PaginationMetaSchema.extend({
389
+ events: z.array(ReceivedWebhookEventSchema),
390
+ });
391
+
392
+ export const SuppressionEntrySchema = z.object({
393
+ email: z.string(),
394
+ created: z.number(),
395
+ reason: z.string().optional(),
396
+ status: z.string().optional(),
397
+ });
398
+
399
+ export const ListSuppressionsOutputSchema = PaginationMetaSchema.extend({
400
+ type: z.string(),
401
+ entries: z.array(SuppressionEntrySchema),
402
+ });
403
+
404
+ export const StatsDimensionSchema = z.enum([
405
+ 'global',
406
+ 'category',
407
+ 'category_sums',
408
+ 'mailbox_provider',
409
+ 'geo',
410
+ 'browser',
411
+ 'device',
412
+ 'client',
413
+ ]);
414
+
415
+ export const EmailStatsOutputSchema = z.object({
416
+ startDate: z.string(),
417
+ endDate: z.string().nullable(),
418
+ dimension: StatsDimensionSchema.optional(),
419
+ aggregatedBy: z.enum(['day', 'week', 'month']).optional(),
420
+ note: z.string().optional(),
421
+ totals: z.object({
422
+ requests: z.number(),
423
+ delivered: z.number(),
424
+ bounces: z.number(),
425
+ opens: z.number(),
426
+ }),
427
+ series: z.array(PassthroughRecordSchema).optional(),
428
+ });
429
+
430
+ export const EmailLogsOutputSchema = z.object({
431
+ count: z.number().int().nonnegative(),
432
+ messages: z.array(
433
+ z
434
+ .object({
435
+ sg_message_id: z.string().optional(),
436
+ from_email: z.string().optional(),
437
+ to_email: z.string().optional(),
438
+ subject: z.string().optional(),
439
+ status: z.string().optional(),
440
+ reason: z.string().optional(),
441
+ sg_message_id_created_at: z.string().optional(),
442
+ })
443
+ .passthrough(),
444
+ ),
445
+ });
446
+
447
+ export const ScopesOutputSchema = z.object({
448
+ count: z.number().int().nonnegative(),
449
+ scopes: z.array(z.string()),
450
+ });
451
+
452
+ export const ManageEventWebhookOutputSchema = z.object({
453
+ action: z.enum(['create', 'delete', 'test']),
454
+ id: z.string().nullable(),
455
+ url: z.string().nullable(),
456
+ webhook: EventWebhookSchema.optional(),
457
+ });
458
+
459
+ export const SubuserSchema = z
460
+ .object({
461
+ id: z.number().optional(),
462
+ username: z.string(),
463
+ email: z.string().optional(),
464
+ disabled: z.boolean().optional(),
465
+ region: z.string().optional(),
466
+ })
467
+ .passthrough();
468
+
469
+ export const SubuserListOutputSchema = z.object({
470
+ count: z.number().int().nonnegative(),
471
+ limit: z.number().int().positive(),
472
+ offset: z.number().int().nonnegative(),
473
+ has_more: z.boolean(),
474
+ subusers: z.array(SubuserSchema),
475
+ });
476
+
477
+ export const DeleteSuppressionOutputSchema = z.object({
478
+ deleted: z.boolean(),
479
+ type: z.string(),
480
+ email: z.string(),
481
+ groupId: z.number().int().nullable(),
482
+ });
483
+
484
+ export const AsmGroupSuppressionSchema = z.object({
485
+ id: z.number().int(),
486
+ name: z.string(),
487
+ suppressed: z.boolean(),
488
+ });