@sendinel/mcp-server 1.0.74 → 1.0.75
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/build/tools/campaigns.js +28 -85
- package/package.json +1 -1
package/build/tools/campaigns.js
CHANGED
|
@@ -2288,7 +2288,7 @@ Do not include body_html in your response; Sendinel renders body_html from block
|
|
|
2288
2288
|
idempotentHint: false,
|
|
2289
2289
|
openWorldHint: true,
|
|
2290
2290
|
},
|
|
2291
|
-
description: 'Send a test email to a specified address using the site\'s configured provider. Content from step_id, draft_id, or raw subject/body_html. Logged as test, not counted in campaign metrics.',
|
|
2291
|
+
description: 'Send a test email to a specified address using the site\'s configured provider. Content from step_id, draft_id, or raw subject/body_html uses the real send renderer. Optional dry_run previews without sending; suppressed recipients are refused. Logged as test, not counted in campaign metrics.',
|
|
2292
2292
|
inputSchema: {
|
|
2293
2293
|
approval_id: z.string().uuid().optional().describe('Get this by calling once without it, then have a human approve the returned approve_url. Re-call with the same arguments plus this approval_id to run the action.'),
|
|
2294
2294
|
to_email: z.string().email().describe('Email address to send the test to'),
|
|
@@ -2298,91 +2298,34 @@ Do not include body_html in your response; Sendinel renders body_html from block
|
|
|
2298
2298
|
subject: z.string().optional(),
|
|
2299
2299
|
body_html: z.string().optional(),
|
|
2300
2300
|
body_text: z.string().optional(),
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
subj = draft.subject;
|
|
2321
|
-
html = draft.body_html ?? '';
|
|
2322
|
-
text = draft.body_text ?? '';
|
|
2323
|
-
}
|
|
2324
|
-
else {
|
|
2301
|
+
dry_run: z.boolean().optional().describe('Render a preview through the real send renderer without sending or writing email_log.'),
|
|
2302
|
+
},
|
|
2303
|
+
}, async (args, extra) => {
|
|
2304
|
+
// Fail before the approval flow can touch the DB when stdio is not configured.
|
|
2305
|
+
if (!requestContext()?.v1Caller && !process.env.SENDINEL_API_KEY) {
|
|
2306
|
+
const result = await callSendinelV1('/api/v1/campaigns/test-send');
|
|
2307
|
+
if (!result.ok)
|
|
2308
|
+
return j({ error: result.error, status: result.status });
|
|
2309
|
+
}
|
|
2310
|
+
return withApproval('send_test_email', async ({ to_email, site_id, step_id, draft_id, subject, body_html, body_text, dry_run }) => {
|
|
2311
|
+
// Same precedence the tool always had, so callers passing several sources keep
|
|
2312
|
+
// working: raw subject/body_html wins, then step_id, then draft_id. The route
|
|
2313
|
+
// takes exactly one source.
|
|
2314
|
+
const source = subject || body_html ? { subject, body_html, body_text }
|
|
2315
|
+
: step_id ? { step_id }
|
|
2316
|
+
: draft_id ? { draft_id }
|
|
2317
|
+
: body_text ? { body_text }
|
|
2318
|
+
: null;
|
|
2319
|
+
if (!source)
|
|
2325
2320
|
return txt('Provide content via subject/body_html, step_id, or draft_id.');
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
.
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
.from('suppressions')
|
|
2335
|
-
.select('reason')
|
|
2336
|
-
.eq('project_id', projectId)
|
|
2337
|
-
.eq('email', to_email.toLowerCase())
|
|
2338
|
-
.maybeSingle();
|
|
2339
|
-
if (suppressed)
|
|
2340
|
-
return txt(`Error: Recipient suppressed (${suppressed.reason}). Test emails to suppressed addresses risk domain reputation.`);
|
|
2341
|
-
const { data: site } = await db()
|
|
2342
|
-
.from('sites')
|
|
2343
|
-
.select('from_email, from_name')
|
|
2344
|
-
.eq('id', site_id)
|
|
2345
|
-
.eq('project_id', projectId)
|
|
2346
|
-
.maybeSingle();
|
|
2347
|
-
if (!site)
|
|
2348
|
-
return txt('Error: Site not found');
|
|
2349
|
-
const resolvedHtml = resolve(html);
|
|
2350
|
-
const resolvedText = resolve(text);
|
|
2351
|
-
const payload = {
|
|
2352
|
-
from: `${site.from_name} <${site.from_email}>`,
|
|
2353
|
-
to: to_email,
|
|
2354
|
-
subject: `[TEST] ${resolve(subj)}`,
|
|
2355
|
-
html: resolvedHtml || undefined,
|
|
2356
|
-
text: resolvedText || undefined,
|
|
2357
|
-
};
|
|
2358
|
-
let sendResult;
|
|
2359
|
-
try {
|
|
2360
|
-
sendResult = await sendEmailForSite(projectId, site_id, payload);
|
|
2361
|
-
}
|
|
2362
|
-
catch (error) {
|
|
2363
|
-
return txt(`Error: Send failed — ${error instanceof Error ? error.message : 'unknown provider error'}`);
|
|
2364
|
-
}
|
|
2365
|
-
await db().from('email_log').insert({
|
|
2366
|
-
project_id: projectId,
|
|
2367
|
-
site_id,
|
|
2368
|
-
contact_id: null,
|
|
2369
|
-
to_email,
|
|
2370
|
-
from_email: site.from_email,
|
|
2371
|
-
subject: resolve(subj),
|
|
2372
|
-
status: 'sent',
|
|
2373
|
-
category: 'test',
|
|
2374
|
-
sent_at: new Date().toISOString(),
|
|
2375
|
-
provider_message_id: sendResult.messageId ?? null,
|
|
2376
|
-
body_html: resolvedHtml ?? null,
|
|
2377
|
-
});
|
|
2378
|
-
return j({
|
|
2379
|
-
sent: true,
|
|
2380
|
-
to: to_email,
|
|
2381
|
-
provider: sendResult.provider,
|
|
2382
|
-
provider_message_id: sendResult.messageId,
|
|
2383
|
-
resend_id: sendResult.provider === 'resend' ? sendResult.messageId : null,
|
|
2384
|
-
});
|
|
2385
|
-
}));
|
|
2321
|
+
// All sources use the dashboard's shared renderer. In stdio mode callSendinelV1
|
|
2322
|
+
// requires SENDINEL_API_KEY; never fall back to a separate local renderer.
|
|
2323
|
+
const result = await callSendinelV1('/api/v1/campaigns/test-send', {
|
|
2324
|
+
body: { to_email, site_id, ...source, dry_run },
|
|
2325
|
+
});
|
|
2326
|
+
return result.ok ? j(result.data) : j({ error: result.error, status: result.status });
|
|
2327
|
+
})(args, extra);
|
|
2328
|
+
});
|
|
2386
2329
|
server.registerTool('send_transactional', {
|
|
2387
2330
|
title: 'Send Transactional',
|
|
2388
2331
|
annotations: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.75",
|
|
4
4
|
"description": "AI-controlled email operations control plane for campaigns, contacts, segments, analytics, and multi-provider sending via MCP.",
|
|
5
5
|
"mcpName": "ai.sendinel/mcp-server",
|
|
6
6
|
"type": "module",
|