@sendinel/mcp-server 1.0.127 → 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 +2 -2
- package/build/lib/webhook-events.js +3 -2
- package/build/tools/sms.js +1 -1
- package/build/tools/social-posts.js +24 -10
- package/build/tools/webhooks.js +15 -1
- package/package.json +1 -1
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
|
|
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` |
|
|
59
|
+
| `webhooks` | 5 | Webhook subscription management |
|
|
60
60
|
|
|
61
61
|
## Install / run
|
|
62
62
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { deliverProjectWebhook } from "@sendinel/domain/sandbox-webhook";
|
|
1
2
|
import { createHmac, randomUUID } from 'node:crypto';
|
|
2
|
-
import { db } from '../db.js';
|
|
3
|
+
import { adminDb, db } from '../db.js';
|
|
3
4
|
import { getProjectId } from '../project.js';
|
|
4
5
|
/**
|
|
5
6
|
* Fire a webhook event to all active subscriptions matching the event type.
|
|
@@ -38,7 +39,7 @@ export async function fireWebhookEvent(event, data, projectId) {
|
|
|
38
39
|
const signature = createHmac('sha256', sub.secret)
|
|
39
40
|
.update(body)
|
|
40
41
|
.digest('hex');
|
|
41
|
-
const res = await
|
|
42
|
+
const res = await deliverProjectWebhook(adminDb(), pid, sub.url, {
|
|
42
43
|
method: 'POST',
|
|
43
44
|
headers: {
|
|
44
45
|
'Content-Type': 'application/json',
|
package/build/tools/sms.js
CHANGED
|
@@ -92,7 +92,7 @@ async function readJsonSafe(res) {
|
|
|
92
92
|
return res.json().catch(() => ({}));
|
|
93
93
|
}
|
|
94
94
|
async function sendViaProvider(provider, credentialsEnc, fromNumber, toNumber, body, webhookUrl, trackingToken) {
|
|
95
|
-
await assertProjectAllowsSms(adminDb(), getProjectId());
|
|
95
|
+
await assertProjectAllowsSms(adminDb(), getProjectId(), toNumber);
|
|
96
96
|
const credentials = parseCredentials(credentialsEnc);
|
|
97
97
|
const meta = countSmsSegments(body);
|
|
98
98
|
if (provider === 'twilio') {
|
|
@@ -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 } 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,14 @@ 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;
|
|
50
|
+
await assertProjectSandboxTargets(adminDb(), params.projectId, 'social', params.profileId ? [`uploadpost:${params.platform}:${params.profileId}`] : []);
|
|
43
51
|
const res = await fetch(`${UPLOADPOST_API_BASE}/uploadposts/schedule`, {
|
|
44
52
|
method: 'POST',
|
|
45
53
|
headers: {
|
|
@@ -47,6 +55,7 @@ async function scheduleUploadPost(apiKey, params) {
|
|
|
47
55
|
'Content-Type': 'application/json',
|
|
48
56
|
},
|
|
49
57
|
body: JSON.stringify({
|
|
58
|
+
user: params.profileId,
|
|
50
59
|
platform: params.platform,
|
|
51
60
|
content: params.link ? `${params.content}\n\n${params.link}` : params.content,
|
|
52
61
|
scheduled_at: params.scheduledAt?.toISOString(),
|
|
@@ -711,26 +720,31 @@ export function registerSocialPostTools(server) {
|
|
|
711
720
|
return j({ error: 'Campaign not found', code: 'NOT_FOUND' });
|
|
712
721
|
const { data: project, error: projectError } = await admin
|
|
713
722
|
.from('projects')
|
|
714
|
-
.select('org_id')
|
|
723
|
+
.select('org_id, sandbox_mode, sandbox_allowed_social_profiles')
|
|
715
724
|
.eq('id', campaign.project_id)
|
|
716
725
|
.maybeSingle();
|
|
717
726
|
if (projectError)
|
|
718
727
|
return txt(`Error: ${projectError.message}`);
|
|
719
728
|
if (!project?.org_id)
|
|
720
729
|
return j({ error: 'Project not found', code: 'NOT_FOUND' });
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
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();
|
|
727
739
|
if (connectionError)
|
|
728
740
|
return txt(`Error: ${connectionError.message}`);
|
|
729
|
-
if (!connection?.encrypted_access_token) {
|
|
741
|
+
if (!mockProfile && !connection?.encrypted_access_token) {
|
|
730
742
|
return j({ error: `No ${p.publisher} connection found. Connect it in Settings -> Integrations.`, code: 'NO_CONNECTION' });
|
|
731
743
|
}
|
|
732
744
|
try {
|
|
733
|
-
const result = await publishViaByoPublisher(p.publisher, decrypt(connection.encrypted_access_token), {
|
|
745
|
+
const result = await publishViaByoPublisher(p.publisher, mockProfile ? "" : decrypt(connection.encrypted_access_token), {
|
|
746
|
+
projectId,
|
|
747
|
+
profileId: connection.account_id ?? undefined,
|
|
734
748
|
content: p.content,
|
|
735
749
|
platform: campaign.platform,
|
|
736
750
|
scheduledAt,
|
package/build/tools/webhooks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { withApproval } from '../lib/action-approvals.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
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.
|
|
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",
|