@sendinel/mcp-server 1.0.26 → 1.0.28

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.
@@ -243,21 +243,24 @@ export function registerConnectorTools(server) {
243
243
  }),
244
244
  metadata_columns: z.array(z.string()).max(100).optional()
245
245
  .describe('Extra source columns copied into contact custom_properties.'),
246
+ identity_merge: z.enum(['off', 'by_email']).optional()
247
+ .describe('How the contact view treats a person found both in the source table and as a Sendinel-owned contact: off = two rows (default), by_email = one row per email (source wins the name, tags combined, unsubscribe and test flags kept from either side). Omit to leave it unchanged.'),
246
248
  ...approvalFields,
247
249
  },
248
- }, async ({ table_id, mapping, metadata_columns, confirmed, approval_id }) => {
249
- const approvalParams = { table_id, mapping, metadata_columns };
250
+ }, async ({ table_id, mapping, metadata_columns, identity_merge, confirmed, approval_id }) => {
251
+ const approvalParams = { table_id, mapping, metadata_columns, ...(identity_merge ? { identity_merge } : {}) };
250
252
  const gate = await authorizeConnectorMutation('set_connector_mapping', confirmed, approval_id, approvalParams, {
251
253
  effect: 'Replace the connector mapping, clear removed custom properties, refresh the live view, and resync contacts.',
252
254
  table_id,
253
255
  mapping,
254
256
  metadata_columns: metadata_columns ?? [],
257
+ ...(identity_merge ? { identity_merge } : {}),
255
258
  });
256
259
  if (gate)
257
260
  return gate;
258
261
  const result = await callConnectorApi('/api/dashboard/connector/mapping', {
259
262
  method: 'PATCH',
260
- body: { table_id, mapping, metadata_columns },
263
+ body: { table_id, mapping, metadata_columns, ...(identity_merge ? { identity_merge } : {}) },
261
264
  });
262
265
  return j(result.ok ? result.data : { error: result.error });
263
266
  });
@@ -408,8 +408,10 @@ function primaryContactValue(value) {
408
408
  }
409
409
  return null;
410
410
  }
411
- const SUBSCRIBER_SELECT_WITH_COMPANY = 'id, email, first_name, last_name, company_id, company:companies(id, name, domain), global_unsubscribed, source, created_at, last_emailed_at, total_emails_sent, total_opens, total_clicks, engagement_score, lifecycle_stage';
412
- const SUBSCRIBER_SELECT_NO_COMPANY = 'id, email, first_name, last_name, company_id, global_unsubscribed, source, created_at, last_emailed_at, total_emails_sent, total_opens, total_clicks, engagement_score, lifecycle_stage';
411
+ // #3623: last_transactional_at / total_transactional_sent are service (transactional) mail, tracked apart
412
+ // from the marketing last_emailed_at / total_emails_sent. NULL means none recorded, not "never".
413
+ const SUBSCRIBER_SELECT_WITH_COMPANY = 'id, email, first_name, last_name, company_id, company:companies(id, name, domain), global_unsubscribed, source, created_at, last_emailed_at, total_emails_sent, last_transactional_at, total_transactional_sent, total_opens, total_clicks, engagement_score, lifecycle_stage';
414
+ const SUBSCRIBER_SELECT_NO_COMPANY = 'id, email, first_name, last_name, company_id, global_unsubscribed, source, created_at, last_emailed_at, total_emails_sent, last_transactional_at, total_transactional_sent, total_opens, total_clicks, engagement_score, lifecycle_stage';
413
415
  const SEGMENT_SUBSCRIBER_SELECT_WITH_COMPANY = 'id, email, first_name, last_name, company_id, company:companies(id, name, domain), engagement_score, lifecycle_stage, last_opened_at, last_clicked_at, total_opens, total_clicks';
414
416
  const SEGMENT_SUBSCRIBER_SELECT_NO_COMPANY = 'id, email, first_name, last_name, company_id, engagement_score, lifecycle_stage, last_opened_at, last_clicked_at, total_opens, total_clicks';
415
417
  const UPDATE_SUBSCRIBER_SELECT_WITH_COMPANY = 'id, email, first_name, last_name, company_id, company:companies(id, name, domain), custom_properties, tags, phone_e164, timezone';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
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",
@@ -23,26 +23,26 @@
23
23
  "prepublishOnly": "npm run build"
24
24
  },
25
25
  "dependencies": {
26
- "@anthropic-ai/sdk": "^0.117.1",
26
+ "@anthropic-ai/sdk": "^0.131.0",
27
27
  "@desler/models": "file:../models",
28
28
  "@sendinel/domain": "file:../domain",
29
- "@modelcontextprotocol/sdk": "^1.30.0",
29
+ "@modelcontextprotocol/sdk": "^1.32.0",
30
30
  "@supabase/supabase-js": "^2.98.0",
31
31
  "dotenv": "^16.4.7",
32
32
  "express": "^5.2.1",
33
- "fast-xml-parser": "^5.10.1",
34
- "file-type": "^22.0.1",
35
- "jose": "^6.2.8",
33
+ "fast-xml-parser": "^5.11.2",
34
+ "file-type": "^22.1.1",
35
+ "jose": "^6.2.12",
36
36
  "qrcode": "^1.5.4",
37
- "resend": "^6.20.0",
37
+ "resend": "^6.32.0",
38
38
  "undici": "^8.10.0",
39
39
  "zod": "^4.4.3"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/express": "^5.0.3",
43
- "@types/node": "^26.2.0",
43
+ "@types/node": "^26.6.4",
44
44
  "@types/qrcode": "^1.5.6",
45
- "tsx": "^4.23.11",
45
+ "tsx": "^4.23.15",
46
46
  "typescript": "^7.0.2",
47
47
  "vitest": "^4.1.10"
48
48
  },