@sendinel/mcp-server 1.0.5 → 1.0.7

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.
@@ -111,7 +111,7 @@ export function registerAbTestingTools(server) {
111
111
  server.registerTool('check_ab_significance', {
112
112
  title: 'Check A/B Significance',
113
113
  annotations: {
114
- readOnlyHint: true,
114
+ readOnlyHint: false,
115
115
  destructiveHint: false,
116
116
  idempotentHint: true,
117
117
  openWorldHint: false,
@@ -295,7 +295,7 @@ export function registerAdvisorTools(server) {
295
295
  server.registerTool('optimize_subject_lines', {
296
296
  title: 'Optimize Subject Lines',
297
297
  annotations: {
298
- readOnlyHint: true,
298
+ readOnlyHint: false,
299
299
  destructiveHint: false,
300
300
  idempotentHint: true,
301
301
  openWorldHint: true,
@@ -448,7 +448,7 @@ export function registerAnalyticsTools(server) {
448
448
  readOnlyHint: true,
449
449
  destructiveHint: false,
450
450
  idempotentHint: true,
451
- openWorldHint: true,
451
+ openWorldHint: false,
452
452
  },
453
453
  description: 'SPF/DKIM/DMARC status and warmup progress for a site',
454
454
  inputSchema: {
@@ -158,9 +158,9 @@ export function registerAutomationTools(server) {
158
158
  title: 'Enroll Automation',
159
159
  annotations: {
160
160
  readOnlyHint: false,
161
- destructiveHint: false,
161
+ destructiveHint: true,
162
162
  idempotentHint: false,
163
- openWorldHint: false,
163
+ openWorldHint: true,
164
164
  },
165
165
  description: 'Enroll a subscriber into active triggered automation campaigns. Match by campaign_id, trigger_event_name, or trigger_tag. Safe to call repeatedly.',
166
166
  inputSchema: {
@@ -239,9 +239,9 @@ export function registerAutomationTools(server) {
239
239
  title: 'Enroll In Escalation Ladder',
240
240
  annotations: {
241
241
  readOnlyHint: false,
242
- destructiveHint: false,
242
+ destructiveHint: true,
243
243
  idempotentHint: false,
244
- openWorldHint: false,
244
+ openWorldHint: true,
245
245
  },
246
246
  description: 'Enroll a subscriber into an active triggered campaign that has escalation ladder steps. Reuses enroll_automation suppression and unsubscribe checks.',
247
247
  inputSchema: {
@@ -149,7 +149,7 @@ export function registerBrandKitTools(server) {
149
149
  readOnlyHint: false,
150
150
  destructiveHint: false,
151
151
  idempotentHint: false,
152
- openWorldHint: true,
152
+ openWorldHint: false,
153
153
  },
154
154
  description: 'Upload a file to the current project\'s media library directly (base64-encoded bytes). For assets already hosted at a public HTTPS URL, use ingest_brand_assets_by_url instead. Video uploads are not supported via MCP yet.',
155
155
  inputSchema: {
@@ -212,10 +212,10 @@ export function registerCampaignTools(server) {
212
212
  server.registerTool('review_campaign_portfolio', {
213
213
  title: 'Review Campaign Portfolio',
214
214
  annotations: {
215
- readOnlyHint: true,
215
+ readOnlyHint: false,
216
216
  destructiveHint: false,
217
217
  idempotentHint: true,
218
- openWorldHint: false,
218
+ openWorldHint: true,
219
219
  },
220
220
  description: 'Run the campaign portfolio review and optionally raise agent proposals for start/end/modify recommendations.',
221
221
  inputSchema: {
@@ -484,7 +484,7 @@ export function registerCampaignTools(server) {
484
484
  title: 'Update Campaign Status',
485
485
  annotations: {
486
486
  readOnlyHint: false,
487
- destructiveHint: false,
487
+ destructiveHint: true,
488
488
  idempotentHint: true,
489
489
  openWorldHint: true,
490
490
  },
@@ -538,9 +538,9 @@ export function registerCampaignTools(server) {
538
538
  title: 'Enroll Contact',
539
539
  annotations: {
540
540
  readOnlyHint: false,
541
- destructiveHint: false,
541
+ destructiveHint: true,
542
542
  idempotentHint: false,
543
- openWorldHint: false,
543
+ openWorldHint: true,
544
544
  },
545
545
  description: 'Enroll a single contact in a campaign',
546
546
  inputSchema: {
@@ -645,9 +645,9 @@ export function registerCampaignTools(server) {
645
645
  title: 'Enroll Segment',
646
646
  annotations: {
647
647
  readOnlyHint: false,
648
- destructiveHint: false,
648
+ destructiveHint: true,
649
649
  idempotentHint: false,
650
- openWorldHint: false,
650
+ openWorldHint: true,
651
651
  },
652
652
  description: 'Enroll all active subscribers from a site into a campaign. Skips globally unsubscribed and suppressed contacts.',
653
653
  inputSchema: {
@@ -1625,7 +1625,7 @@ Adapt your tone accordingly:
1625
1625
  server.registerTool('check_content_consistency', {
1626
1626
  title: 'Check Content Consistency',
1627
1627
  annotations: {
1628
- readOnlyHint: true,
1628
+ readOnlyHint: false,
1629
1629
  destructiveHint: false,
1630
1630
  idempotentHint: true,
1631
1631
  openWorldHint: true,
@@ -1703,7 +1703,7 @@ Return ONLY valid JSON with this exact shape:
1703
1703
  title: 'Create Campaign With Content',
1704
1704
  annotations: {
1705
1705
  readOnlyHint: false,
1706
- destructiveHint: false,
1706
+ destructiveHint: true,
1707
1707
  idempotentHint: false,
1708
1708
  openWorldHint: true,
1709
1709
  },
@@ -1437,8 +1437,8 @@ Requirements: urgency without desperation, acknowledge the gap, one clear CTA, v
1437
1437
  server.registerTool('identify_inactive', {
1438
1438
  title: 'Identify Inactive',
1439
1439
  annotations: {
1440
- readOnlyHint: true,
1441
- destructiveHint: false,
1440
+ readOnlyHint: false,
1441
+ destructiveHint: true,
1442
1442
  idempotentHint: true,
1443
1443
  openWorldHint: false,
1444
1444
  },
@@ -206,7 +206,7 @@ export function registerContentTools(server) {
206
206
  title: 'Create Blog Post',
207
207
  annotations: {
208
208
  readOnlyHint: false,
209
- destructiveHint: false,
209
+ destructiveHint: true,
210
210
  idempotentHint: false,
211
211
  openWorldHint: true,
212
212
  },
@@ -138,9 +138,10 @@ function createCampaignMigrationTotals(options) {
138
138
  },
139
139
  };
140
140
  }
141
- function createPlatformMigrationTotals(artifactTypes) {
141
+ function createPlatformMigrationTotals(artifactTypes, platform) {
142
142
  const requested = artifactTypes?.length ? new Set(artifactTypes) : null;
143
- const enabled = (key) => !requested || requested.has(key);
143
+ const customerIoOnly = new Set(['newsletters', 'broadcasts', 'transactional', 'snippets', 'collections', 'messages']);
144
+ const enabled = (key) => (!customerIoOnly.has(key) || platform === 'customerio') && (!requested || requested.has(key));
144
145
  return {
145
146
  contacts: { total: enabled('contacts') ? 1 : 0, processed: 0 },
146
147
  templates: { total: enabled('templates') ? 1 : 0, processed: 0 },
@@ -148,6 +149,12 @@ function createPlatformMigrationTotals(artifactTypes) {
148
149
  suppressions: { total: enabled('suppressions') ? 1 : 0, processed: 0 },
149
150
  segments: { total: enabled('segments') ? 1 : 0, processed: 0 },
150
151
  enrollments: { total: enabled('enrollments') ? 1 : 0, processed: 0 },
152
+ newsletters: { total: enabled('newsletters') ? 1 : 0, processed: 0 },
153
+ broadcasts: { total: enabled('broadcasts') ? 1 : 0, processed: 0 },
154
+ transactional: { total: enabled('transactional') ? 1 : 0, processed: 0 },
155
+ snippets: { total: enabled('snippets') ? 1 : 0, processed: 0 },
156
+ collections: { total: enabled('collections') ? 1 : 0, processed: 0 },
157
+ messages: { total: enabled('messages') ? 1 : 0, processed: 0 },
151
158
  processed: 0,
152
159
  errors: 0,
153
160
  account_name: 'Platform migration',
@@ -158,6 +165,12 @@ function createPlatformMigrationTotals(artifactTypes) {
158
165
  import_suppressions: enabled('suppressions'),
159
166
  import_segments: enabled('segments'),
160
167
  import_enrollments: enabled('enrollments'),
168
+ import_newsletters: enabled('newsletters'),
169
+ import_broadcasts: enabled('broadcasts'),
170
+ import_transactional: enabled('transactional'),
171
+ import_snippets: enabled('snippets'),
172
+ import_collections: enabled('collections'),
173
+ import_messages: enabled('messages'),
161
174
  migration_entry_point: 'mcp_connection',
162
175
  },
163
176
  };
@@ -255,7 +268,7 @@ export function registerDataTools(server) {
255
268
  readOnlyHint: false,
256
269
  destructiveHint: false,
257
270
  idempotentHint: false,
258
- openWorldHint: true,
271
+ openWorldHint: false,
259
272
  },
260
273
  description: 'Store encrypted credentials for an external platform connection and mark it ready for trigger configuration.',
261
274
  inputSchema: {
@@ -637,7 +650,10 @@ export function registerDataTools(server) {
637
650
  description: 'Start a migration from a stored external platform connection into Sendinel. Returns a job_id for status polling.',
638
651
  inputSchema: {
639
652
  connection_id: z.string().uuid(),
640
- artifact_types: z.array(z.enum(['contacts', 'templates', 'campaigns', 'suppressions', 'segments', 'enrollments'])).optional(),
653
+ artifact_types: z.array(z.enum([
654
+ 'contacts', 'templates', 'campaigns', 'suppressions', 'segments', 'enrollments',
655
+ 'newsletters', 'broadcasts', 'transactional', 'snippets', 'collections', 'messages',
656
+ ])).optional(),
641
657
  dry_run: z.boolean().optional().default(false),
642
658
  },
643
659
  }, async ({ connection_id, artifact_types, dry_run = false }) => {
@@ -669,7 +685,7 @@ export function registerDataTools(server) {
669
685
  provider: connection.platform,
670
686
  status: 'pending',
671
687
  progress_pct: 0,
672
- totals: createPlatformMigrationTotals(artifact_types),
688
+ totals: createPlatformMigrationTotals(artifact_types, connection.platform),
673
689
  errors: [],
674
690
  credentials_enc: encrypt(JSON.stringify(credentials)),
675
691
  field_mapping: null,
@@ -723,7 +739,7 @@ export function registerDataTools(server) {
723
739
  server.registerTool('tenant_export_full', {
724
740
  title: 'Tenant Export Full',
725
741
  annotations: {
726
- readOnlyHint: true,
742
+ readOnlyHint: false,
727
743
  destructiveHint: false,
728
744
  idempotentHint: true,
729
745
  openWorldHint: false,
@@ -9,9 +9,9 @@ export function registerDeliveryOpsTools(server) {
9
9
  readOnlyHint: true,
10
10
  destructiveHint: false,
11
11
  idempotentHint: true,
12
- openWorldHint: true,
12
+ openWorldHint: false,
13
13
  },
14
- description: 'Refresh and return stored DNS verification status for a sending domain.',
14
+ description: 'Return stored DNS verification status for a sending domain. This does not perform a live DNS refresh.',
15
15
  inputSchema: { domain_id: z.string().uuid() },
16
16
  }, async ({ domain_id }) => {
17
17
  const { data, error } = await db().from('domains')
@@ -35,7 +35,7 @@ export function registerDeliveryOpsTools(server) {
35
35
  readOnlyHint: true,
36
36
  destructiveHint: false,
37
37
  idempotentHint: true,
38
- openWorldHint: true,
38
+ openWorldHint: false,
39
39
  },
40
40
  description: 'Return the latest DNSBL snapshot for all monitored zones for a site sending domain.',
41
41
  inputSchema: {
@@ -77,7 +77,7 @@ export function registerDeliveryOpsTools(server) {
77
77
  readOnlyHint: true,
78
78
  destructiveHint: false,
79
79
  idempotentHint: true,
80
- openWorldHint: true,
80
+ openWorldHint: false,
81
81
  },
82
82
  description: 'Draft a DNSBL delisting ticket using tenant send metrics as evidence. Does not auto-submit.',
83
83
  inputSchema: {
@@ -432,9 +432,9 @@ export function registerDeliveryOpsTools(server) {
432
432
  readOnlyHint: false,
433
433
  destructiveHint: false,
434
434
  idempotentHint: false,
435
- openWorldHint: true,
435
+ openWorldHint: false,
436
436
  },
437
- description: 'Register a sending domain for a site and link it to the site record.',
437
+ description: 'Create or update a pending sending-domain record and link it to a site. This does not register the domain with an email provider.',
438
438
  inputSchema: {
439
439
  site_id: z.string().uuid(),
440
440
  domain: z.string().min(1),
@@ -307,7 +307,7 @@ export function registerOrgTools(server) {
307
307
  title: 'Start Provider Switch',
308
308
  annotations: {
309
309
  readOnlyHint: false,
310
- destructiveHint: false,
310
+ destructiveHint: true,
311
311
  idempotentHint: false,
312
312
  openWorldHint: true,
313
313
  },
@@ -327,12 +327,12 @@ export function registerOrgTools(server) {
327
327
  server.registerTool('get_provider_switch_status', {
328
328
  title: 'Get Provider Switch Status',
329
329
  annotations: {
330
- readOnlyHint: true,
331
- destructiveHint: false,
330
+ readOnlyHint: false,
331
+ destructiveHint: true,
332
332
  idempotentHint: true,
333
333
  openWorldHint: true,
334
334
  },
335
- description: 'Poll and return the current status of a provider switch.',
335
+ description: 'Poll and return the current status of a provider switch. Note: while the switch is pending/verifying, this call also drives it forward — it checks domain verification and, if newly verified, flips the site to the new provider as a side effect.',
336
336
  inputSchema: {
337
337
  id: z.string().uuid(),
338
338
  },
@@ -302,7 +302,7 @@ export function registerSmsTools(server) {
302
302
  title: 'Set Contact SMS Consent',
303
303
  annotations: {
304
304
  readOnlyHint: false,
305
- destructiveHint: false,
305
+ destructiveHint: true,
306
306
  idempotentHint: true,
307
307
  openWorldHint: false,
308
308
  },
@@ -258,7 +258,7 @@ export function registerSocialPostTools(server) {
258
258
  title: 'Create Social Post',
259
259
  annotations: {
260
260
  readOnlyHint: false,
261
- destructiveHint: false,
261
+ destructiveHint: true,
262
262
  idempotentHint: false,
263
263
  openWorldHint: true,
264
264
  },
@@ -377,7 +377,7 @@ export function registerSocialPostTools(server) {
377
377
  title: 'Update Social Post',
378
378
  annotations: {
379
379
  readOnlyHint: false,
380
- destructiveHint: false,
380
+ destructiveHint: true,
381
381
  idempotentHint: true,
382
382
  openWorldHint: true,
383
383
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
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",