@sendinel/mcp-server 1.0.111 → 1.0.112

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.
@@ -3,9 +3,28 @@ import { rankTools, scoreTool, tokenize, TOOL_ALIASES } from '../tools/tool-sear
3
3
  const approval = { required: true, how: 'returns approve_url; re-call with approval_id' };
4
4
  const from = (n, field) => `<from step ${n}: ${field}>`;
5
5
  const projectRule = 'On hosted /mcp, pass project_id on each call. It is required for approval-gated writes after switching away from the home project. With invoke_tool put project_id on the outer call; keep approval-only inner args to approval_id.';
6
- const previewRule = 'preview_email only substitutes sample merge variables; it is not the real send renderer. send_test_email with dry_run:true uses the real renderer without sending, but still follows the approval flow in standard mode; keep the same args plus approval_id when re-calling it. Stdio requires SENDINEL_API_KEY.';
6
+ const previewRule = 'preview_email uses the real send renderer with sample contact data. send_test_email with dry_run:true uses the real renderer without sending, but still follows the approval flow in standard mode; keep the same args plus approval_id when re-calling it. Stdio requires SENDINEL_API_KEY.';
7
7
  const sender = { n: 1, tool: 'get_sites', args_example: {}, note: 'Choose the intended sender from the returned array; do not assume the first sender is correct. Replace example content and addresses with the user’s own choices.' };
8
8
  export const HOW_TO_RECIPES = [
9
+ {
10
+ id: 'event-driven-billing-mail-end-to-end', title: 'Event-driven billing mail end to end',
11
+ when_to_use: 'Build and verify event-driven billing, dunning, payment failed or trial ending transactional mail, then request human approval for activation.',
12
+ synonyms: ['event-driven billing mail end to end', 'billing', 'dunning', 'trial ending', 'transactional', 'payment failed'],
13
+ requires: { scopes: ['admin'], tester_ok: false },
14
+ steps: [sender,
15
+ { n: 2, tool: 'create_campaign_with_content', args_example: { site_id: from(1, '0.id'), name: 'Payment failed', type: 'triggered', trigger_event_name: 'invoice_payment_failed', send_class: 'product', steps: [{ subject: 'Please update your payment method', body_html: '<p>Please sign in to review your payment method.</p>', body_text: 'Please sign in to review your payment method.', delay_days: 0 }], activate: false, confirmed: false }, note: 'Preview the draft creation. Replace the event and content with the actual integration event and reviewed copy. product is the account/trial/billing consent class; transactional is not an enum value.' },
16
+ { n: 3, tool: 'create_campaign_with_content', args_example: { site_id: from(1, '0.id'), name: 'Payment failed', type: 'triggered', trigger_event_name: 'invoice_payment_failed', send_class: 'product', steps: [{ subject: 'Please update your payment method', body_html: '<p>Please sign in to review your payment method.</p>', body_text: 'Please sign in to review your payment method.', delay_days: 0 }], activate: false, confirmed: true }, note: 'Repeat the reviewed creation args with confirmed:true. Keep activate:false; no segment is required. Follow any returned setup approval before execution.' },
17
+ { n: 4, tool: 'set_campaign_trigger', args_example: { campaign_id: from(3, 'campaign_id'), trigger_event_name: 'invoice_payment_failed', trigger_config: { events: ['invoice_payment_failed'], where: { plan: 'paid' } } }, approval, note: 'Use event-data keys actually emitted by the integration. Drafts save directly; if approval is requested, wait for the human and follow the returned re-call instructions. trial_ending and trial_ending_no_card are alternative event names.' },
18
+ { n: 5, tool: 'update_site', args_example: { site_id: from(1, '0.id'), unsub_footer_template: "You're receiving this because you have an account. <unsubscribe>Unsubscribe</unsubscribe>" }, note: 'Site-wide change: review its effect on every campaign using this sender, keep previous_unsub_footer_template for undo, and follow any returned approval. Leave physical_address unchanged. For only this campaign, skip this call and instead pass unsub_footer_template with this same tagged text to BOTH creation calls; a null campaign override uses the site template.' },
19
+ { n: 6, tool: 'list_campaign_steps', args_example: { campaign_id: from(3, 'campaign_id') }, note: 'Choose the intended saved step; preview each step in a multi-step sequence.' },
20
+ { n: 7, tool: 'preview_email', args_example: { step_id: from(6, 'steps.0.id') }, note: 'Check the rendered footer (campaign override else site), unsubscribe scope for product, sender and physical address. Sample placeholder links are not delivery proof.' },
21
+ { n: 8, tool: 'activate_campaigns', args_example: { campaign_ids: [from(3, 'campaign_id')] }, approval, note: 'Human approval is required. Show the exact content, sender, event/filter and approve_url. Stop until a human approves; never approve on their behalf.' },
22
+ { n: 9, tool: 'activate_campaigns', args_example: { approval_id: from(8, 'approval_id') }, approval, note: 'Only after human approval, re-call with ONLY approval_id. This activates the saved campaign and can cause future matching events to send mail.' },
23
+ { n: 10, tool: 'get_campaign', args_example: { campaign_id: from(3, 'campaign_id') }, note: 'Final read back: verify fires_on, trigger_diagnostics, status, send_class, unsub_footer_template and the saved trigger_config.where.' },
24
+ ],
25
+ gotchas: [projectRule, previewRule, 'Never activate without human approval. A trigger binding does not emit an event or enroll historical events. Product mail still follows consent/suppression gates; preview does not prove eligibility or delivery. Setup writes may require their own approval depending on the connection role.'],
26
+ done_when: 'The reviewed draft renders the intended footer and product scope, activation has human approval, and get_campaign confirms fires_on and trigger_diagnostics. Real delivery remains unverified until a separately authorized event test.',
27
+ },
9
28
  {
10
29
  id: 'set-campaign-trigger', title: 'Set what a campaign fires on',
11
30
  when_to_use: 'Bind an existing campaign to an event such as invoice_payment_failed for dunning, or change its tag/date/contact-event trigger.',
@@ -2279,17 +2279,18 @@ Do not include body_html in your response; Sendinel renders body_html from block
2279
2279
  idempotentHint: true,
2280
2280
  openWorldHint: false,
2281
2281
  },
2282
- description: 'Preview email through the real send renderer using sample contact data; unresolved_tags lists remaining tags. Provide content via step_id, draft_id, or raw body_html/subject.',
2282
+ description: 'Preview email through the real send renderer: final campaign/site footer, unsubscribe text and send_class scope, sender and physical address. Uses sample contact data; unresolved_tags lists remaining tags. Provide campaign_id, step_id, draft_id, or raw body_html/subject.',
2283
2283
  inputSchema: {
2284
2284
  site_id: z.string().uuid().optional().describe('Site UUID for sender tags and footers'),
2285
+ campaign_id: z.string().uuid().optional().describe('Campaign UUID; previews its first step in send order. Use step_id to choose a specific step.'),
2285
2286
  step_id: z.string().uuid().optional().describe('Campaign step UUID'),
2286
2287
  draft_id: z.string().uuid().optional().describe('AI draft UUID'),
2287
2288
  subject: z.string().optional().describe('Raw subject'),
2288
2289
  body_html: z.string().optional().describe('Raw HTML'),
2289
2290
  },
2290
- }, async ({ step_id, draft_id, subject, body_html, site_id }) => {
2291
+ }, async ({ campaign_id, step_id, draft_id, subject, body_html, site_id }) => {
2291
2292
  const result = await callSendinelV1('/api/v1/campaigns/preview', {
2292
- body: { step_id, draft_id, subject, body_html, site_id },
2293
+ body: { campaign_id, step_id, draft_id, subject, body_html, site_id },
2293
2294
  });
2294
2295
  return result.ok ? j(result.data) : j({ error: result.error, status: result.status });
2295
2296
  });
@@ -2961,7 +2962,7 @@ Return ONLY valid JSON with this exact shape:
2961
2962
  idempotentHint: false,
2962
2963
  openWorldHint: true,
2963
2964
  },
2964
- description: 'Create a sequence, broadcast or triggered campaign with pre-built email content in one call. Set trigger_event_name for dunning (invoice_payment_failed, trial_ending), trigger_tag, or date_field/contact_event trigger_config; events/tags and where filters are supported. Triggered campaigns need no segment. Returns fires_on and trigger_diagnostics. Adds email steps, optionally enrolls a segment, and activates behind standard approval. Ideal for orchestrators that draft their own content — skips AI generation. Uses two-call confirmation: first call returns a preview, second call with confirmed=true executes.',
2965
+ description: 'Create billing, dunning, trial ending or transactional mail with send_class=product (broadcast is the marketing default). Build a sequence, broadcast or triggered campaign with pre-built steps. Override unsubscribe text with unsub_footer_template. Set trigger_event_name for dunning (invoice_payment_failed, trial_ending), trigger_tag, or date_field/contact_event trigger_config; events/tags and where filters are supported. Triggered campaigns need no segment. Returns fires_on and trigger_diagnostics. Adds email steps, optionally enrolls a segment, and activates behind standard approval. Ideal for orchestrators that draft their own content — skips AI generation. Uses two-call confirmation: first call returns a preview, second call with confirmed=true executes.',
2965
2966
  inputSchema: {
2966
2967
  site_id: z.string().uuid().describe('Site to send from'),
2967
2968
  name: z.string().min(1).max(200).describe('Campaign name'),
@@ -3357,7 +3358,7 @@ Return ONLY valid JSON with this exact shape:
3357
3358
  server.registerTool('set_campaign_trigger', {
3358
3359
  title: 'Set Campaign Trigger',
3359
3360
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
3360
- description: 'Set or clear what a campaign fires on: trigger event name, tag, date_field, contact_event, or trigger_config events/tags and where event-data filter. Use for dunning on invoice_payment_failed or trial_ending. Drafts save directly; active/paused campaigns require standard human approval. Archived/completed campaigns cannot change. Returns saved trigger fields, fires_on and trigger_diagnostics.',
3361
+ description: 'Set or clear what a campaign fires on: trigger event name, tag, date_field, contact_event, or trigger_config events/tags and where event-data filter. Bind billing, payment failed, dunning or trial ending mail to an event trigger such as invoice_payment_failed or trial_ending. Drafts save directly; active/paused campaigns require standard human approval. Archived/completed campaigns cannot change. Returns saved trigger fields, fires_on and trigger_diagnostics.',
3361
3362
  inputSchema: {
3362
3363
  campaign_id: z.string().uuid().optional(),
3363
3364
  trigger_event_name: z.string().nullable().optional(),
@@ -13,7 +13,9 @@ export const STOPWORDS = new Set([
13
13
  */
14
14
  export const TOOL_ALIASES = {
15
15
  update_site: ['footer', 'email footer', 'unsubscribe text', 'unsubscribe link', 'site footer', 'unsubscribe settings'],
16
- set_campaign_trigger: ['trigger', 'event', 'fires on', 'fires-on', 'dunning', 'set campaign trigger event name', 'invoice payment failed', 'trial ending'],
16
+ set_campaign_trigger: ['trigger', 'event', 'fires on', 'fires-on', 'dunning', 'set campaign trigger event name', 'invoice payment failed', 'payment failed', 'trial ending', 'event trigger', 'billing'],
17
+ create_campaign_with_content: ['billing', 'dunning', 'trial ending', 'transactional', 'payment failed', 'product email', 'event-driven billing mail'],
18
+ preview_email: ['preview footer', 'footer', 'unsubscribe text', 'preview billing email', 'unsubscribe scope'],
17
19
  send_broadcast: [
18
20
  'send email', 'send an email', 'email blast', 'blast', 'newsletter', 'announce', 'announcement', 'one-off email',
19
21
  'email my list', 'send to my list', 'email to my list', 'email my subscribers',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.111",
3
+ "version": "1.0.112",
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",