@sendinel/mcp-server 1.0.113 → 1.0.115
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 +12 -9
- package/package.json +1 -1
package/build/tools/campaigns.js
CHANGED
|
@@ -31,6 +31,7 @@ import { callSendinelV1 } from '../lib/v1-client.js';
|
|
|
31
31
|
import { countUniqueEngagementByCampaign } from '../lib/campaign-engagement.js';
|
|
32
32
|
const txt = (text) => ({ content: [{ type: 'text', text }] });
|
|
33
33
|
const j = (data) => txt(JSON.stringify(data, null, 2));
|
|
34
|
+
const ACTIVATION_APPROVAL_DESCRIPTION = 'Activation-only: requires_approval gates campaign activation. It does not stop sends of an already-active campaign or sends triggered by other paths.';
|
|
34
35
|
const SENT_EMAIL_STATUSES = ['sent', 'delivered', 'opened', 'clicked', 'bounced', 'complained', 'unsubscribed'];
|
|
35
36
|
function stripHtml(value) {
|
|
36
37
|
return value
|
|
@@ -557,13 +558,13 @@ export function registerCampaignTools(server) {
|
|
|
557
558
|
idempotentHint: true,
|
|
558
559
|
openWorldHint: true,
|
|
559
560
|
},
|
|
560
|
-
description: 'Run the campaign portfolio review
|
|
561
|
+
description: 'Run the campaign portfolio review. Defaults to a dry run (no writes); pass dry_run=false to raise agent proposals for start/end/modify recommendations and emit recommendation signals.',
|
|
561
562
|
inputSchema: {
|
|
562
|
-
dry_run: z.boolean().optional().default(false),
|
|
563
|
+
dry_run: z.boolean().optional().default(true).describe('Preview only by default: computes findings and writes nothing. Pass false to raise agent proposals and emit recommendation signals.'),
|
|
563
564
|
dormant_draft_days: z.number().int().min(1).max(365).optional(),
|
|
564
565
|
abandoned_days: z.number().int().min(1).max(365).optional(),
|
|
565
566
|
},
|
|
566
|
-
}, async ({ dry_run =
|
|
567
|
+
}, async ({ dry_run = true, dormant_draft_days, abandoned_days }) => {
|
|
567
568
|
const result = await callSendinelV1('/api/v1/campaigns/review', {
|
|
568
569
|
body: {
|
|
569
570
|
dry_run,
|
|
@@ -642,7 +643,7 @@ export function registerCampaignTools(server) {
|
|
|
642
643
|
idempotentHint: true,
|
|
643
644
|
openWorldHint: false,
|
|
644
645
|
},
|
|
645
|
-
description: 'List campaigns with saved trigger fields, fires_on summaries and trigger_diagnostics; optionally filter by site or status.',
|
|
646
|
+
description: 'List campaigns with saved trigger fields, fires_on summaries and trigger_diagnostics; optionally filter by site or status. ' + ACTIVATION_APPROVAL_DESCRIPTION,
|
|
646
647
|
inputSchema: {
|
|
647
648
|
site_id: z.string().uuid().optional(),
|
|
648
649
|
period: z.string().min(1).optional().describe("Return one enrollment period in by_period; totals remain all-time."),
|
|
@@ -652,7 +653,7 @@ export function registerCampaignTools(server) {
|
|
|
652
653
|
}, async ({ site_id, status, limit, period }) => {
|
|
653
654
|
let q = db()
|
|
654
655
|
.from('campaigns')
|
|
655
|
-
.select('id, site_id, name, type, status, sends_per_hour, total_enrolled, total_sent, total_opened, total_clicked, created_at, period_key, segment_id, trigger_type, trigger_event_name, trigger_tag, trigger_config, send_class, unsub_footer_template')
|
|
656
|
+
.select('id, site_id, name, type, status, sends_per_hour, total_enrolled, total_sent, total_opened, total_clicked, created_at, period_key, segment_id, trigger_type, trigger_event_name, trigger_tag, trigger_config, send_class, unsub_footer_template, requires_approval, approved_by, approved_at')
|
|
656
657
|
.eq('project_id', getProjectId())
|
|
657
658
|
.order('created_at', { ascending: false })
|
|
658
659
|
.limit(limit ?? 25);
|
|
@@ -672,6 +673,7 @@ export function registerCampaignTools(server) {
|
|
|
672
673
|
const { period_key: _periodKey, ...legacy } = row;
|
|
673
674
|
return {
|
|
674
675
|
...legacy,
|
|
676
|
+
requires_approval_description: ACTIVATION_APPROVAL_DESCRIPTION,
|
|
675
677
|
fires_on: buildCampaignFiresOn(row),
|
|
676
678
|
trigger_diagnostics: buildCampaignTriggerDiagnostics(row),
|
|
677
679
|
...campaignPeriodFields(periods, period),
|
|
@@ -735,7 +737,7 @@ export function registerCampaignTools(server) {
|
|
|
735
737
|
type: z.enum(['sequence', 'broadcast', 'triggered']),
|
|
736
738
|
description: z.string().optional(),
|
|
737
739
|
sends_per_hour: z.number().int().min(1).nullable().optional().describe('Optional hourly send cap for wave sending. Null or omitted sends without a campaign-specific cap.'),
|
|
738
|
-
requires_approval: z.boolean().optional().default(true),
|
|
740
|
+
requires_approval: z.boolean().optional().default(true).describe(ACTIVATION_APPROVAL_DESCRIPTION),
|
|
739
741
|
trigger_event_name: z.string().optional().describe('For type=triggered: event name that fires this campaign'),
|
|
740
742
|
trigger_tag: z.string().optional().describe('For type=triggered: contact tag that fires this campaign when applied'),
|
|
741
743
|
trigger_type: z.enum(['date_field']).optional().describe('Set for elapsed-time triggers keyed on a contact custom_properties date field'),
|
|
@@ -773,7 +775,7 @@ export function registerCampaignTools(server) {
|
|
|
773
775
|
.single();
|
|
774
776
|
if (error)
|
|
775
777
|
return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
|
|
776
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
778
|
+
return { content: [{ type: 'text', text: JSON.stringify({ ...data, requires_approval_description: ACTIVATION_APPROVAL_DESCRIPTION }, null, 2) }] };
|
|
777
779
|
});
|
|
778
780
|
const propertyConditions = z.array(z.union([
|
|
779
781
|
z.object({ key: z.string(), op: z.enum(['eq', 'neq']), value: z.string() }).strict(),
|
|
@@ -849,7 +851,7 @@ export function registerCampaignTools(server) {
|
|
|
849
851
|
idempotentHint: true,
|
|
850
852
|
openWorldHint: true,
|
|
851
853
|
},
|
|
852
|
-
description: 'Activate, pause, or archive a campaign. Activating requires human approval via the dashboard — call without approval_id to request one, then call again with the approved token.',
|
|
854
|
+
description: 'Activate, pause, or archive a campaign. Activating requires human approval via the dashboard — call without approval_id to request one, then call again with the approved token. ' + ACTIVATION_APPROVAL_DESCRIPTION,
|
|
853
855
|
inputSchema: {
|
|
854
856
|
campaign_id: z.string().uuid(),
|
|
855
857
|
status: z.enum(['draft', 'active', 'paused', 'completed', 'archived']),
|
|
@@ -3558,7 +3560,7 @@ Return ONLY valid JSON with this exact shape:
|
|
|
3558
3560
|
idempotentHint: true,
|
|
3559
3561
|
openWorldHint: false,
|
|
3560
3562
|
},
|
|
3561
|
-
description: 'Get full campaign details including all steps, enrollment counts and saved trigger_type, trigger_event_name, trigger_tag, trigger_config (including where filter), fires_on and trigger_diagnostics. Read what event a dunning campaign fires on; use set_campaign_trigger to change it.',
|
|
3563
|
+
description: 'Get full campaign details including all steps, enrollment counts and saved trigger_type, trigger_event_name, trigger_tag, trigger_config (including where filter), fires_on and trigger_diagnostics. Read what event a dunning campaign fires on; use set_campaign_trigger to change it. ' + ACTIVATION_APPROVAL_DESCRIPTION,
|
|
3562
3564
|
inputSchema: { campaign_id: z.string().uuid(), period: z.string().min(1).optional().describe("Return results for one enrollment period; totals remain all-time.") },
|
|
3563
3565
|
}, async ({ campaign_id, period }) => {
|
|
3564
3566
|
const projectId = getProjectId();
|
|
@@ -3629,6 +3631,7 @@ Return ONLY valid JSON with this exact shape:
|
|
|
3629
3631
|
const unresolvedCount = dlqUnresolvedCount ?? 0;
|
|
3630
3632
|
return j({
|
|
3631
3633
|
...campaign,
|
|
3634
|
+
requires_approval_description: ACTIVATION_APPROVAL_DESCRIPTION,
|
|
3632
3635
|
fires_on: buildCampaignFiresOn(campaign),
|
|
3633
3636
|
trigger_diagnostics: buildCampaignTriggerDiagnostics(campaign),
|
|
3634
3637
|
total_sent: periods?.totals?.sent ?? sentCount ?? 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.115",
|
|
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",
|