@sendinel/mcp-server 1.0.94 → 1.0.95

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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ AI-controlled email operations control plane for campaigns, contacts, segments,
12
12
 
13
13
  ## Tool groups
14
14
 
15
- Sendinel currently exposes 35 tool groups and 338 tools (gateway mode exposes 4 meta-tools, sharing how_to with full mode). These counts are checked against the tool registry in CI, so this table matches what ships.
15
+ Sendinel currently exposes 35 tool groups and 339 tools (gateway mode exposes 4 meta-tools, sharing how_to with full mode). These counts are checked against the tool registry in CI, so this table matches what ships.
16
16
 
17
17
  | Group | Count | Purpose |
18
18
  |-------|-------|---------|
@@ -28,7 +28,7 @@ Sendinel currently exposes 35 tool groups and 338 tools (gateway mode exposes 4
28
28
  | `cloud-import` | 6 | Cloud storage import |
29
29
  | `compound` | 4 | Higher-level campaign/template/site setup and delivery diagnosis workflows |
30
30
  | `connector` | 6 | BYOD connector status, table discovery, schema/view setup, mapping, resync |
31
- | `contacts` | 37 | Subscriber CRUD, saved views, tags, email log, suppression, merge, timeline |
31
+ | `contacts` | 38 | Subscriber CRUD, saved views, tags, email log, suppression, merge, timeline |
32
32
  | `content` | 14 | Content blocks, gated asset links, blog ingest and auto-publish, sources |
33
33
  | `data` | 19 | Platform connectors, imports, API keys, migration jobs, notifications, tenant export |
34
34
  | `data-proposals` | 3 | Data-derived campaign proposal generation, listing, approval |
@@ -53,6 +53,7 @@ const GATED_ACTIONS = new Set([
53
53
  'revoke_api_key', // breaks every integration using the key
54
54
  'tenant_export_full', // copies all customer data out of Sendinel
55
55
  'enroll_contact', // starts real emails to a contact
56
+ 'identify_subscriber', // may enroll a new signup in sending campaigns
56
57
  'enroll_automation', // starts automated messages to a contact
57
58
  'enroll_in_escalation_ladder', // starts email/SMS escalation to a contact
58
59
  'set_contact_sms_consent', // changes the consent record that allows texting
package/build/server.js CHANGED
@@ -82,7 +82,7 @@ export const ALL_TOOL_GROUPS = Object.keys(GROUP_REGISTRY);
82
82
  const WRITE_TOOLS = new Set([
83
83
  'recheck_domain_dns',
84
84
  'mark_agent_message', 'draft_agent_reply', 'create_agent_mailbox',
85
- 'add_subscriber', 'import_subscribers', 'update_subscriber', 'bulk_update_subscribers',
85
+ 'add_subscriber', 'identify_subscriber', 'import_subscribers', 'update_subscriber', 'bulk_update_subscribers',
86
86
  'create_subscriber_view', 'update_subscriber_view', 'delete_subscriber_view',
87
87
  'enrich_subscriber',
88
88
  'create_campaign', 'update_campaign', 'delete_campaign', 'add_campaign_step', 'update_campaign_status',
@@ -3,6 +3,7 @@ import { CONTACT_ENRICHMENT_PROVIDERS } from '@desler/models';
3
3
  import { createContactEnrichmentAdapters, enrichContactFromConnections, } from '@desler/models/contact-enrichment';
4
4
  import { buildPreviewAttentionEnvelope, applySegmentRules, ContactPropertyAnomalyLogger, normalizeContactProperties, getAttentionDecision, getContactNeighborhood, getScheduledTouches, findContactDuplicateGroups, readAllPages, readAllRows, chunkIds, } from '@sendinel/domain';
5
5
  import { z } from 'zod';
6
+ import { callSendinelV1 } from '../lib/v1-client.js';
6
7
  import { db, projectClientForWriteback } from '../db.js';
7
8
  import { getProjectId } from '../project.js';
8
9
  import { checkPlanLimit } from '../plan-limits.js';
@@ -535,6 +536,25 @@ async function exportSubscriberData(projectId, contactId) {
535
536
  };
536
537
  }
537
538
  export function registerContactTools(server) {
539
+ server.registerTool('identify_subscriber', {
540
+ title: 'Identify Subscriber',
541
+ description: 'Identify or update a contact through the v1 identify API. Optional signup/customer consent subscribes new or existing contacts without overriding opt-outs. Customer consent requires a topic; consent requires site_id. Without consent, site_id retains legacy create-only subscription behavior and returns a warning.',
542
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
543
+ inputSchema: {
544
+ approval_id: z.string().uuid().optional().describe('Call once to request approval, then repeat the same arguments with the approved approval_id.'),
545
+ email: z.string().email(),
546
+ first_name: z.string().optional(),
547
+ last_name: z.string().optional(),
548
+ properties: z.record(z.string(), z.unknown()).optional(),
549
+ tags: z.array(z.string()).optional(),
550
+ site_id: z.string().uuid().optional(),
551
+ consent: z.enum(['signup', 'customer']).optional(),
552
+ topic: z.string().regex(/^[a-z0-9_]{1,64}$/).optional(),
553
+ },
554
+ }, withApproval('identify_subscriber', async (params) => {
555
+ const result = await callSendinelV1('/api/v1/identify', { method: 'POST', body: params });
556
+ return result.ok ? j(result.data) : err(JSON.stringify(result.error));
557
+ }));
538
558
  server.registerTool('get_subscriber_timeline', {
539
559
  title: 'Get Subscriber Timeline',
540
560
  annotations: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.94",
3
+ "version": "1.0.95",
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",