@sendinel/mcp-server 1.0.221 → 1.0.222
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,41 @@
|
|
|
1
|
+
const HREF_RE = /<a\b[^>]*?\shref\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
|
|
2
|
+
const PREHEADER_RE = /^\s*<span\b[^>]*\bstyle\s*=\s*(?:"[^"]*display:\s*none[^"]*"|'[^']*display:\s*none[^']*')[^>]*>([\s\S]*?)<\/span>/i;
|
|
3
|
+
function htmlUnescape(text) {
|
|
4
|
+
return text
|
|
5
|
+
.replace(/&/g, '&')
|
|
6
|
+
.replace(/</g, '<')
|
|
7
|
+
.replace(/>/g, '>')
|
|
8
|
+
.replace(/"/g, '"')
|
|
9
|
+
.replace(/'/g, "'");
|
|
10
|
+
}
|
|
11
|
+
export function extractStepLinks(bodyHtml) {
|
|
12
|
+
const links = [];
|
|
13
|
+
const seen = new Set();
|
|
14
|
+
HREF_RE.lastIndex = 0;
|
|
15
|
+
let match;
|
|
16
|
+
while ((match = HREF_RE.exec(bodyHtml)) !== null) {
|
|
17
|
+
const href = match[1] ?? match[2] ?? '';
|
|
18
|
+
if (!seen.has(href)) {
|
|
19
|
+
seen.add(href);
|
|
20
|
+
links.push(href);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return links;
|
|
24
|
+
}
|
|
25
|
+
export function extractStepPreheader(bodyHtml) {
|
|
26
|
+
const match = bodyHtml.match(PREHEADER_RE);
|
|
27
|
+
if (!match)
|
|
28
|
+
return null;
|
|
29
|
+
return htmlUnescape(match[1]);
|
|
30
|
+
}
|
|
31
|
+
export function summarizeStepForStatus(step) {
|
|
32
|
+
const { body_html, body_text, blocks, ...rest } = step;
|
|
33
|
+
const bodyHtml = typeof body_html === 'string' ? body_html : '';
|
|
34
|
+
return {
|
|
35
|
+
...rest,
|
|
36
|
+
subject: step.subject,
|
|
37
|
+
preheader: bodyHtml ? extractStepPreheader(bodyHtml) : null,
|
|
38
|
+
links: extractStepLinks(bodyHtml),
|
|
39
|
+
body_length: bodyHtml.length,
|
|
40
|
+
};
|
|
41
|
+
}
|
package/build/tools/campaigns.js
CHANGED
|
@@ -36,6 +36,7 @@ import { checkCampaignMergeVariables, persistMergeVariableWarnings, summarizeMer
|
|
|
36
36
|
import { recordBrandAssetUsage } from '../lib/brand-asset-usage.js';
|
|
37
37
|
import { BLOCK_GENERATION_SCHEMA, blocksToHtml, blocksToText, normalizeComposerBlocks } from '../lib/composer-blocks.js';
|
|
38
38
|
import { callSendinelV1 } from '../lib/v1-client.js';
|
|
39
|
+
import { summarizeStepForStatus } from '../lib/summarize-step-for-status.js';
|
|
39
40
|
import { countUniqueEngagementByCampaign } from '../lib/campaign-engagement.js';
|
|
40
41
|
const txt = (text) => ({ content: [{ type: 'text', text }] });
|
|
41
42
|
const j = (data) => txt(JSON.stringify(data, null, 2));
|
|
@@ -4028,9 +4029,13 @@ Return ONLY valid JSON with this exact shape:
|
|
|
4028
4029
|
idempotentHint: true,
|
|
4029
4030
|
openWorldHint: false,
|
|
4030
4031
|
},
|
|
4031
|
-
description: 'Get full campaign details including all steps, enrollment counts and derived trigger_kind (event/tag/date_field/contact_event/manual), 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. trigger_kind is the canonical answer to what fires the campaign; trigger_type is the stored schedule mode and stays "manual" for event and tag triggers. ' + ACTIVATION_APPROVAL_DESCRIPTION,
|
|
4032
|
-
inputSchema: {
|
|
4033
|
-
|
|
4032
|
+
description: 'Get full campaign details including all steps, enrollment counts and derived trigger_kind (event/tag/date_field/contact_event/manual), 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. trigger_kind is the canonical answer to what fires the campaign; trigger_type is the stored schedule mode and stays "manual" for event and tag triggers. Optional include_bodies (default true): when false, each step returns subject, preheader, links and body_length instead of body_html, body_text, and blocks. ' + ACTIVATION_APPROVAL_DESCRIPTION,
|
|
4033
|
+
inputSchema: {
|
|
4034
|
+
campaign_id: z.string().uuid(),
|
|
4035
|
+
period: z.string().min(1).optional().describe("Return results for one enrollment period; totals remain all-time."),
|
|
4036
|
+
include_bodies: z.boolean().optional().default(true).describe('When false, each step returns subject, preheader, links and body_length instead of body_html/body_text/blocks. Default true.'),
|
|
4037
|
+
},
|
|
4038
|
+
}, async ({ campaign_id, period, include_bodies }) => {
|
|
4034
4039
|
const projectId = getProjectId();
|
|
4035
4040
|
const { data: campaign, error: campErr } = await db()
|
|
4036
4041
|
.from('campaigns')
|
|
@@ -4100,6 +4105,7 @@ Return ONLY valid JSON with this exact shape:
|
|
|
4100
4105
|
const queuedCount = queuedEmailCount ?? 0;
|
|
4101
4106
|
const failedCount = failedSendCount ?? 0;
|
|
4102
4107
|
const unresolvedCount = dlqUnresolvedCount ?? 0;
|
|
4108
|
+
const mappedSteps = (steps ?? []).map((step) => ({ ...step, ...stepMultiEventRulesView(step), ...stepAbsenceRuleView(step), total_sent: stepMetrics.sent.get(step.id) ?? 0 }));
|
|
4103
4109
|
return j({
|
|
4104
4110
|
...campaignMetricsView(campaign),
|
|
4105
4111
|
merge_variable_warnings: campaign.metadata?.merge_variable_warnings ?? [],
|
|
@@ -4109,7 +4115,7 @@ Return ONLY valid JSON with this exact shape:
|
|
|
4109
4115
|
total_sent: periods?.totals?.sent ?? sentCount ?? 0,
|
|
4110
4116
|
...(periods?.totals ? { unique_opened: periods.totals.opened, unique_clicked: periods.totals.clicked } : {}),
|
|
4111
4117
|
...campaignPeriodFields(periods, period),
|
|
4112
|
-
steps:
|
|
4118
|
+
steps: include_bodies === false ? mappedSteps.map(summarizeStepForStatus) : mappedSteps,
|
|
4113
4119
|
active_enrollments: enrollCount ?? 0,
|
|
4114
4120
|
send_incomplete: queuedCount > 0 || failedCount > 0 || outstandingRows.length > 0 || unresolvedCount > 0,
|
|
4115
4121
|
queued_email_count: queuedCount,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.222",
|
|
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",
|