@sendinel/mcp-server 1.0.160 → 1.0.162

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.
@@ -3553,7 +3553,7 @@ Return ONLY valid JSON with this exact shape:
3553
3553
  server.registerTool('set_campaign_trigger', {
3554
3554
  title: 'Set Campaign Trigger',
3555
3555
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
3556
- description: 'Send timing: the first step of an event-triggered product journey sends right away; later steps wait for the site send window (contact zone, then campaign zone, then UTC). 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, derived trigger_kind, fires_on and trigger_diagnostics (including unseen-event warnings).',
3556
+ description: 'Send timing: the first step of an event-triggered product journey sends right away when it has no delay; later steps and delayed first steps wait for the site send window (contact zone, then campaign zone, then UTC). 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, derived trigger_kind, fires_on and trigger_diagnostics (including unseen-event warnings).',
3557
3557
  inputSchema: {
3558
3558
  ...reentryFields,
3559
3559
  campaign_id: z.string().uuid().optional(),
@@ -2,7 +2,7 @@ import { withApproval } from '../lib/action-approvals.js';
2
2
  import { z } from 'zod';
3
3
  import { adminDb, db } from '../db.js';
4
4
  import { getProjectId } from '../project.js';
5
- import { stripActiveEmailContent, validateUnsubscribeFooterTemplate } from '@sendinel/domain';
5
+ import { refuseActiveTextFields, stripActiveEmailContent, validateUnsubscribeFooterTemplate } from '@sendinel/domain';
6
6
  import { dbToolError, toolError } from '../lib/db-error.js';
7
7
  import { logAudit } from '../audit.js';
8
8
  import { assertHttpUrl } from '../lib/url-validation.js';
@@ -110,6 +110,11 @@ export function registerContentTools(server) {
110
110
  gate_body: z.string().max(1000).optional(),
111
111
  },
112
112
  }, async ({ title, slug, asset_url, asset_type = 'page', site_id, source_attribution, gate_headline, gate_body }) => {
113
+ for (const [field, value] of Object.entries({ title, source_attribution, gate_headline, gate_body })) {
114
+ const error = refuseActiveTextFields(field, value);
115
+ if (error)
116
+ return j({ error, code: 'validation_failed' });
117
+ }
113
118
  try {
114
119
  assertHttpUrl(asset_url);
115
120
  }
@@ -1,3 +1,4 @@
1
+ import { refuseActiveText, refuseActiveTextFields } from '@sendinel/domain';
1
2
  import { z } from 'zod';
2
3
  import { db } from '../db.js';
3
4
  import { getProjectId } from '../project.js';
@@ -28,6 +29,9 @@ function normalizeQuestions(questions) {
28
29
  });
29
30
  }
30
31
  function validateQuestions(questions) {
32
+ const activeTextError = refuseActiveTextFields('questions', questions);
33
+ if (activeTextError)
34
+ return activeTextError;
31
35
  const ids = new Set();
32
36
  for (const question of questions) {
33
37
  if (!question.label.trim())
@@ -85,6 +89,9 @@ export function registerSurveyTools(server) {
85
89
  status: z.enum(['draft', 'active', 'closed']).optional().default('draft'),
86
90
  },
87
91
  }, async ({ title, questions, site_id, status }) => {
92
+ const activeTextError = refuseActiveText('title', title) ?? refuseActiveTextFields('questions', questions);
93
+ if (activeTextError)
94
+ return j({ error: activeTextError, code: 'validation_failed' });
88
95
  const normalized = normalizeQuestions(questions);
89
96
  const validationError = validateQuestions(normalized);
90
97
  if (validationError)
@@ -217,6 +224,9 @@ export function registerSurveyTools(server) {
217
224
  },
218
225
  }, async ({ survey_id, title, questions, status }) => {
219
226
  const projectId = getProjectId();
227
+ const activeTextError = refuseActiveText('title', title) ?? refuseActiveTextFields('questions', questions);
228
+ if (activeTextError)
229
+ return j({ error: activeTextError, code: 'validation_failed' });
220
230
  const updates = {};
221
231
  if (title !== undefined)
222
232
  updates.title = title;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.160",
3
+ "version": "1.0.162",
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",