@sendinel/mcp-server 1.0.128 → 1.0.129

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
@@ -18,7 +18,7 @@ preserves REST outcomes, and redacts failures. See the
18
18
  [event-triggered campaign guide](../../docs/event-triggered-campaigns.md) for
19
19
  inputs, idempotency, subscription requirements, and conditional step examples.
20
20
 
21
- Sendinel currently exposes 35 tool groups and 342 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.
21
+ Sendinel currently exposes 35 tool groups and 343 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.
22
22
 
23
23
  | Group | Count | Purpose |
24
24
  |-------|-------|---------|
@@ -56,7 +56,7 @@ Sendinel currently exposes 35 tool groups and 342 tools (gateway mode exposes 4
56
56
  | `surveys` | 5 | Survey creation, listing, status, results |
57
57
  | `templates` | 7 | Email template CRUD, version restore, migration translation |
58
58
  | `warmup` | 4 | IP/domain warmup schedule control |
59
- | `webhooks` | 4 | Webhook subscription management |
59
+ | `webhooks` | 5 | Webhook subscription management |
60
60
 
61
61
  ## Install / run
62
62
 
@@ -5,7 +5,7 @@ import { db, adminDb } from '../db.js';
5
5
  import { decrypt } from '../crypto.js';
6
6
  import { getProjectId } from '../project.js';
7
7
  import { getAnthropicModelId } from '../anthropic-model.js';
8
- import { AI_MAX_TOKENS, outputConfig, anthropicCheckedText, assertProjectSandboxTargets } from '@sendinel/domain';
8
+ import { AI_MAX_TOKENS, outputConfig, anthropicCheckedText, recordSandboxSocialPost, configuredSandboxSocialProfile, assertProjectSandboxTargets } from '@sendinel/domain';
9
9
  import { buildVoicePrompt } from '../lib/voice.js';
10
10
  import { buildSocialSystemPrompt, buildSocialUserPrompt, parseSocialResponse, selectArchetype, } from '../lib/social-engine/index.js';
11
11
  import { fireWebhookEvent } from '../lib/webhook-events.js';
@@ -40,6 +40,13 @@ function buildSocialTrackedUrl(params) {
40
40
  };
41
41
  }
42
42
  async function scheduleUploadPost(apiKey, params) {
43
+ const mock = await recordSandboxSocialPost(adminDb(), {
44
+ projectId: params.projectId, profileId: params.profileId, platforms: [params.platform],
45
+ text: params.link ? `${params.content}\n\n${params.link}` : params.content,
46
+ mediaUrls: params.mediaUrls, scheduledAt: params.scheduledAt,
47
+ });
48
+ if (mock)
49
+ return mock;
43
50
  await assertProjectSandboxTargets(adminDb(), params.projectId, 'social', params.profileId ? [`uploadpost:${params.platform}:${params.profileId}`] : []);
44
51
  const res = await fetch(`${UPLOADPOST_API_BASE}/uploadposts/schedule`, {
45
52
  method: 'POST',
@@ -713,26 +720,29 @@ export function registerSocialPostTools(server) {
713
720
  return j({ error: 'Campaign not found', code: 'NOT_FOUND' });
714
721
  const { data: project, error: projectError } = await admin
715
722
  .from('projects')
716
- .select('org_id')
723
+ .select('org_id, sandbox_mode, sandbox_allowed_social_profiles')
717
724
  .eq('id', campaign.project_id)
718
725
  .maybeSingle();
719
726
  if (projectError)
720
727
  return txt(`Error: ${projectError.message}`);
721
728
  if (!project?.org_id)
722
729
  return j({ error: 'Project not found', code: 'NOT_FOUND' });
723
- const { data: connection, error: connectionError } = await admin
724
- .from('social_platform_connections')
725
- .select('encrypted_access_token, account_id')
726
- .eq('org_id', project.org_id)
727
- .eq('platform', p.publisher)
728
- .maybeSingle();
730
+ const mockProfile = configuredSandboxSocialProfile(project, campaign.platform);
731
+ const { data: connection, error: connectionError } = mockProfile
732
+ ? { data: { encrypted_access_token: null, account_id: mockProfile }, error: null }
733
+ : await admin
734
+ .from('social_platform_connections')
735
+ .select('encrypted_access_token, account_id')
736
+ .eq('org_id', project.org_id)
737
+ .eq('platform', p.publisher)
738
+ .maybeSingle();
729
739
  if (connectionError)
730
740
  return txt(`Error: ${connectionError.message}`);
731
- if (!connection?.encrypted_access_token) {
741
+ if (!mockProfile && !connection?.encrypted_access_token) {
732
742
  return j({ error: `No ${p.publisher} connection found. Connect it in Settings -> Integrations.`, code: 'NO_CONNECTION' });
733
743
  }
734
744
  try {
735
- const result = await publishViaByoPublisher(p.publisher, decrypt(connection.encrypted_access_token), {
745
+ const result = await publishViaByoPublisher(p.publisher, mockProfile ? "" : decrypt(connection.encrypted_access_token), {
736
746
  projectId,
737
747
  profileId: connection.account_id ?? undefined,
738
748
  content: p.content,
@@ -1,6 +1,7 @@
1
1
  import { withApproval } from '../lib/action-approvals.js';
2
2
  import { z } from 'zod';
3
- import { db } from '../db.js';
3
+ import { listSandboxWebhookEvents } from '@sendinel/domain';
4
+ import { db, adminDb } from '../db.js';
4
5
  import { getProjectId } from '../project.js';
5
6
  import { validateWebhookUrl, generateWebhookSecret } from '../lib/webhook-events.js';
6
7
  const txt = (text) => ({ content: [{ type: 'text', text }] });
@@ -25,6 +26,19 @@ const VALID_EVENTS = [
25
26
  'asset_link.viewed',
26
27
  ];
27
28
  export function registerWebhookTools(server) {
29
+ server.registerTool('list_sandbox_webhook_events', {
30
+ title: 'List Sandbox Webhook Events',
31
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
32
+ description: 'Read recent captured webhook requests for the active sandbox project (seven-day retention). Never returns sink tokens.',
33
+ inputSchema: { limit: z.number().int().min(1).max(100).optional().default(20) },
34
+ }, async ({ limit }) => {
35
+ try {
36
+ return j({ events: await listSandboxWebhookEvents(adminDb(), getProjectId(), limit) });
37
+ }
38
+ catch {
39
+ return txt('Sandbox webhook events unavailable.');
40
+ }
41
+ });
28
42
  server.registerTool('subscribe_webhook', {
29
43
  title: 'Subscribe Webhook',
30
44
  annotations: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sendinel/mcp-server",
3
- "version": "1.0.128",
3
+ "version": "1.0.129",
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",