@sendinel/mcp-server 1.0.146 → 1.0.148
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/build/lib/action-approvals.js +3 -1
- package/build/lib/db-error.d.ts +39 -6
- package/build/lib/db-error.js +221 -11
- package/build/server.js +5 -3
- package/build/tools/ab-testing.js +4 -3
- package/build/tools/advisor.js +2 -2
- package/build/tools/ai-work.js +14 -13
- package/build/tools/analytics.js +33 -32
- package/build/tools/approvals.js +3 -1
- package/build/tools/automations.js +12 -11
- package/build/tools/brand-kit.js +4 -3
- package/build/tools/campaign-sets.js +11 -10
- package/build/tools/campaigns.js +81 -79
- package/build/tools/compound.js +7 -6
- package/build/tools/connector.js +4 -3
- package/build/tools/contacts.js +44 -45
- package/build/tools/content.js +16 -16
- package/build/tools/convert-imported.js +8 -7
- package/build/tools/data-proposals.js +8 -7
- package/build/tools/data.js +39 -38
- package/build/tools/delivery-ops.js +32 -32
- package/build/tools/drafts.js +10 -9
- package/build/tools/forms.js +5 -4
- package/build/tools/gdpr.js +10 -9
- package/build/tools/landing-pages.js +3 -3
- package/build/tools/lists.js +21 -20
- package/build/tools/newsletters.js +26 -25
- package/build/tools/org.js +26 -23
- package/build/tools/segments.js +4 -5
- package/build/tools/short-links.js +7 -6
- package/build/tools/signatures.js +10 -9
- package/build/tools/sites.js +9 -8
- package/build/tools/sms.js +14 -13
- package/build/tools/social-posts.js +20 -19
- package/build/tools/social-replies.js +7 -6
- package/build/tools/surveys.js +10 -9
- package/build/tools/templates.js +14 -13
- package/build/tools/warmup.js +5 -4
- package/build/tools/webhooks.js +5 -4
- package/package.json +1 -1
- package/build/lib/db-errors.d.ts +0 -12
- package/build/lib/db-errors.js +0 -20
package/build/tools/templates.js
CHANGED
|
@@ -6,6 +6,7 @@ import { db, requestContext } from '../db.js';
|
|
|
6
6
|
import { getProjectId } from '../project.js';
|
|
7
7
|
import { recordBrandAssetUsage } from '../lib/brand-asset-usage.js';
|
|
8
8
|
import { deriveTemplateVariables } from '../lib/voice.js';
|
|
9
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
9
10
|
const txt = (text) => ({ content: [{ type: 'text', text }] });
|
|
10
11
|
const j = (data) => txt(JSON.stringify(data, null, 2));
|
|
11
12
|
function stripHtml(value) {
|
|
@@ -91,7 +92,7 @@ export function registerTemplateTools(server) {
|
|
|
91
92
|
query = query.ilike('name', `%${search}%`);
|
|
92
93
|
const { data, error } = await query;
|
|
93
94
|
if (error)
|
|
94
|
-
return j(
|
|
95
|
+
return j(dbToolError(error));
|
|
95
96
|
return j({ templates: data ?? [], count: data?.length ?? 0 });
|
|
96
97
|
});
|
|
97
98
|
server.registerTool('translate_template', {
|
|
@@ -117,7 +118,7 @@ export function registerTemplateTools(server) {
|
|
|
117
118
|
const projectId = getProjectId();
|
|
118
119
|
const { data: project, error: projectError } = await db().from('projects').select('org_id').eq('id', projectId).single();
|
|
119
120
|
if (projectError || !project?.org_id)
|
|
120
|
-
return j({ error: projectError
|
|
121
|
+
return j({ error: (projectError ? dbToolError(projectError).error : undefined) ?? 'Project not found' });
|
|
121
122
|
const source = String(html ?? text ?? '').trim();
|
|
122
123
|
if (!source)
|
|
123
124
|
return j({ error: 'html or text is required' });
|
|
@@ -159,7 +160,7 @@ export function registerTemplateTools(server) {
|
|
|
159
160
|
extracted_json: translated,
|
|
160
161
|
}).select('id').single();
|
|
161
162
|
if (error)
|
|
162
|
-
return j(
|
|
163
|
+
return j(dbToolError(error));
|
|
163
164
|
let savedTemplateId = null;
|
|
164
165
|
if (save) {
|
|
165
166
|
const { data: saved, error: saveError } = await db().from('campaign_templates').insert({
|
|
@@ -179,7 +180,7 @@ export function registerTemplateTools(server) {
|
|
|
179
180
|
external_id: source_template_id ?? null,
|
|
180
181
|
}).select('id').single();
|
|
181
182
|
if (saveError)
|
|
182
|
-
return j(
|
|
183
|
+
return j(dbToolError(saveError));
|
|
183
184
|
savedTemplateId = saved?.id ?? null;
|
|
184
185
|
if (savedTemplateId) {
|
|
185
186
|
await recordBrandAssetUsage({
|
|
@@ -215,7 +216,7 @@ export function registerTemplateTools(server) {
|
|
|
215
216
|
.or(`project_id.is.null,project_id.eq.${projectId}`)
|
|
216
217
|
.maybeSingle();
|
|
217
218
|
if (error)
|
|
218
|
-
return j(
|
|
219
|
+
return j(dbToolError(error));
|
|
219
220
|
return j({ template: data });
|
|
220
221
|
});
|
|
221
222
|
server.registerTool('create_template', {
|
|
@@ -256,7 +257,7 @@ export function registerTemplateTools(server) {
|
|
|
256
257
|
.select('id, name, category, description, subject_hint, brief_template, body_html, body_source, body_text, format, variables, tags, created_at')
|
|
257
258
|
.single();
|
|
258
259
|
if (error)
|
|
259
|
-
return j(
|
|
260
|
+
return j(dbToolError(error));
|
|
260
261
|
await recordBrandAssetUsage({
|
|
261
262
|
client: db(),
|
|
262
263
|
projectId,
|
|
@@ -311,7 +312,7 @@ export function registerTemplateTools(server) {
|
|
|
311
312
|
.eq('id', id)
|
|
312
313
|
.maybeSingle();
|
|
313
314
|
if (existingErr || !existing)
|
|
314
|
-
return j({ error: existingErr
|
|
315
|
+
return j({ error: (existingErr ? dbToolError(existingErr).error : undefined) ?? 'Template not found', code: 'NOT_FOUND' });
|
|
315
316
|
if (!existing.project_id || existing.project_id !== projectId)
|
|
316
317
|
return j({ error: 'Cannot edit system templates', code: 'FORBIDDEN' });
|
|
317
318
|
const row = { updated_at: new Date().toISOString() };
|
|
@@ -342,7 +343,7 @@ export function registerTemplateTools(server) {
|
|
|
342
343
|
.select('id, name, category, description, subject_hint, brief_template, body_html, body_source, body_text, format, variables, tags, updated_at')
|
|
343
344
|
.single();
|
|
344
345
|
if (error)
|
|
345
|
-
return j(
|
|
346
|
+
return j(dbToolError(error));
|
|
346
347
|
await recordBrandAssetUsage({
|
|
347
348
|
client: db(),
|
|
348
349
|
projectId,
|
|
@@ -386,7 +387,7 @@ export function registerTemplateTools(server) {
|
|
|
386
387
|
.eq('id', id)
|
|
387
388
|
.maybeSingle();
|
|
388
389
|
if (existingErr || !existing)
|
|
389
|
-
return j({ error: existingErr
|
|
390
|
+
return j({ error: (existingErr ? dbToolError(existingErr).error : undefined) ?? 'Template not found', code: 'NOT_FOUND' });
|
|
390
391
|
if (!existing.project_id || existing.project_id !== projectId)
|
|
391
392
|
return j({ error: 'Cannot delete system templates', code: 'FORBIDDEN' });
|
|
392
393
|
const { error } = await db().from('campaign_templates')
|
|
@@ -394,7 +395,7 @@ export function registerTemplateTools(server) {
|
|
|
394
395
|
.eq('id', id)
|
|
395
396
|
.eq('project_id', projectId);
|
|
396
397
|
if (error)
|
|
397
|
-
return j(
|
|
398
|
+
return j(dbToolError(error));
|
|
398
399
|
return j({ ok: true, deleted: id });
|
|
399
400
|
}));
|
|
400
401
|
server.registerTool('restore_template_version', {
|
|
@@ -417,7 +418,7 @@ export function registerTemplateTools(server) {
|
|
|
417
418
|
.eq('id', id)
|
|
418
419
|
.maybeSingle();
|
|
419
420
|
if (existingErr || !existing)
|
|
420
|
-
return j({ error: existingErr
|
|
421
|
+
return j({ error: (existingErr ? dbToolError(existingErr).error : undefined) ?? 'Template not found', code: 'NOT_FOUND' });
|
|
421
422
|
if (!existing.project_id || existing.project_id !== projectId)
|
|
422
423
|
return j({ error: 'Cannot restore a system template', code: 'FORBIDDEN' });
|
|
423
424
|
const { data: version, error: versionErr } = await db().from('template_versions')
|
|
@@ -426,7 +427,7 @@ export function registerTemplateTools(server) {
|
|
|
426
427
|
.eq('template_id', id)
|
|
427
428
|
.maybeSingle();
|
|
428
429
|
if (versionErr || !version)
|
|
429
|
-
return j({ error: versionErr
|
|
430
|
+
return j({ error: (versionErr ? dbToolError(versionErr).error : undefined) ?? 'Version not found', code: 'NOT_FOUND' });
|
|
430
431
|
const bodyRestored = versionHasBodySnapshot(version);
|
|
431
432
|
const row = {
|
|
432
433
|
updated_at: new Date().toISOString(),
|
|
@@ -455,7 +456,7 @@ export function registerTemplateTools(server) {
|
|
|
455
456
|
.select('id, name, category, description, subject_hint, brief_template, body_html, body_source, body_text, format, variables, tags, updated_at')
|
|
456
457
|
.single();
|
|
457
458
|
if (error)
|
|
458
|
-
return j(
|
|
459
|
+
return j(dbToolError(error));
|
|
459
460
|
const nextVersion = await snapshotTemplateVersion(db(), {
|
|
460
461
|
templateId: data.id,
|
|
461
462
|
name: data.name,
|
package/build/tools/warmup.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { db } from '../db.js';
|
|
3
3
|
import { getProjectId } from '../project.js';
|
|
4
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
4
5
|
const j = (data) => ({ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] });
|
|
5
6
|
export function registerWarmupTools(server) {
|
|
6
7
|
server.registerTool('get_warmup_status', {
|
|
@@ -22,7 +23,7 @@ export function registerWarmupTools(server) {
|
|
|
22
23
|
.limit(1)
|
|
23
24
|
.maybeSingle();
|
|
24
25
|
if (error)
|
|
25
|
-
return j(
|
|
26
|
+
return j(dbToolError(error));
|
|
26
27
|
const day = data?.warmup_day ?? 0;
|
|
27
28
|
return j({
|
|
28
29
|
phase: day >= 42 ? 'complete' : day > 0 ? 'warming' : 'not_started',
|
|
@@ -47,7 +48,7 @@ export function registerWarmupTools(server) {
|
|
|
47
48
|
const { error } = await db().from('warmup_schedules')
|
|
48
49
|
.upsert({ project_id: getProjectId(), site_id, target_volume: daily_limit ?? null, status: phase ?? 'active', updated_at: new Date().toISOString() }, { onConflict: 'site_id' });
|
|
49
50
|
if (error)
|
|
50
|
-
return j(
|
|
51
|
+
return j(dbToolError(error));
|
|
51
52
|
return j({ updated: true });
|
|
52
53
|
});
|
|
53
54
|
server.registerTool('pause_warmup', {
|
|
@@ -63,7 +64,7 @@ export function registerWarmupTools(server) {
|
|
|
63
64
|
}, async ({ site_id, reason }) => {
|
|
64
65
|
const { error } = await db().from('warmup_schedules').upsert({ project_id: getProjectId(), site_id, status: 'paused', pause_reason: reason ?? null, updated_at: new Date().toISOString() }, { onConflict: 'site_id' });
|
|
65
66
|
if (error)
|
|
66
|
-
return j(
|
|
67
|
+
return j(dbToolError(error));
|
|
67
68
|
return j({ paused: true });
|
|
68
69
|
});
|
|
69
70
|
server.registerTool('resume_warmup', {
|
|
@@ -79,7 +80,7 @@ export function registerWarmupTools(server) {
|
|
|
79
80
|
}, async ({ site_id }) => {
|
|
80
81
|
const { error } = await db().from('warmup_schedules').upsert({ project_id: getProjectId(), site_id, status: 'active', updated_at: new Date().toISOString() }, { onConflict: 'site_id' });
|
|
81
82
|
if (error)
|
|
82
|
-
return j(
|
|
83
|
+
return j(dbToolError(error));
|
|
83
84
|
return j({ resumed: true });
|
|
84
85
|
});
|
|
85
86
|
}
|
package/build/tools/webhooks.js
CHANGED
|
@@ -4,6 +4,7 @@ import { listSandboxWebhookEvents } from '@sendinel/domain';
|
|
|
4
4
|
import { db, adminDb } from '../db.js';
|
|
5
5
|
import { getProjectId } from '../project.js';
|
|
6
6
|
import { validateWebhookUrl, generateWebhookSecret } from '../lib/webhook-events.js';
|
|
7
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
7
8
|
const txt = (text) => ({ content: [{ type: 'text', text }] });
|
|
8
9
|
const j = (data) => txt(JSON.stringify(data, null, 2));
|
|
9
10
|
const VALID_EVENTS = [
|
|
@@ -72,7 +73,7 @@ export function registerWebhookTools(server) {
|
|
|
72
73
|
.select('id, url, events, active, created_at')
|
|
73
74
|
.single();
|
|
74
75
|
if (error)
|
|
75
|
-
return txt(`Error creating subscription: ${error.
|
|
76
|
+
return txt(`Error creating subscription: ${dbToolError(error).error}`);
|
|
76
77
|
return j({
|
|
77
78
|
...data,
|
|
78
79
|
secret, // Only returned on creation — store it securely
|
|
@@ -102,7 +103,7 @@ export function registerWebhookTools(server) {
|
|
|
102
103
|
q = q.eq('active', true);
|
|
103
104
|
const { data, error } = await q;
|
|
104
105
|
if (error)
|
|
105
|
-
return txt(`Error: ${error.
|
|
106
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
106
107
|
return j({ subscriptions: data ?? [], count: data?.length ?? 0 });
|
|
107
108
|
});
|
|
108
109
|
server.registerTool('update_webhook_subscription', {
|
|
@@ -145,7 +146,7 @@ export function registerWebhookTools(server) {
|
|
|
145
146
|
.select('id, url, events, active, description, updated_at')
|
|
146
147
|
.single();
|
|
147
148
|
if (error)
|
|
148
|
-
return txt(`Error: ${error.
|
|
149
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
149
150
|
return j(data);
|
|
150
151
|
}));
|
|
151
152
|
server.registerTool('delete_webhook_subscription', {
|
|
@@ -168,7 +169,7 @@ export function registerWebhookTools(server) {
|
|
|
168
169
|
.eq('id', subscription_id)
|
|
169
170
|
.eq('project_id', getProjectId());
|
|
170
171
|
if (error)
|
|
171
|
-
return txt(`Error: ${error.
|
|
172
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
172
173
|
return txt(`Subscription ${subscription_id} deleted.`);
|
|
173
174
|
}));
|
|
174
175
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.148",
|
|
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",
|
package/build/lib/db-errors.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** Caller-safe database errors. Never serialize database messages, details or hints. */
|
|
2
|
-
export declare function mapDbError(error: unknown): {
|
|
3
|
-
error: string;
|
|
4
|
-
code: string;
|
|
5
|
-
};
|
|
6
|
-
export declare function dbErrorResult(error: unknown): {
|
|
7
|
-
isError: boolean;
|
|
8
|
-
content: {
|
|
9
|
-
type: 'text';
|
|
10
|
-
text: string;
|
|
11
|
-
}[];
|
|
12
|
-
};
|
package/build/lib/db-errors.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** Caller-safe database errors. Never serialize database messages, details or hints. */
|
|
2
|
-
export function mapDbError(error) {
|
|
3
|
-
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
4
|
-
if (code === '23505')
|
|
5
|
-
return { error: 'A record with these values already exists.', code: 'conflict' };
|
|
6
|
-
if (code === '23503' || code === 'PGRST116') {
|
|
7
|
-
return { error: 'The requested or referenced record was not found.', code: 'not_found' };
|
|
8
|
-
}
|
|
9
|
-
if (code.startsWith('23') || code.startsWith('22')) {
|
|
10
|
-
return { error: 'The supplied values are not valid. Check the input and try again.', code: 'validation_failed' };
|
|
11
|
-
}
|
|
12
|
-
return { error: 'The operation could not be completed. Please try again later.', code: 'internal_error' };
|
|
13
|
-
}
|
|
14
|
-
export function dbErrorResult(error) {
|
|
15
|
-
console.warn('[mcp] Database operation failed:', error);
|
|
16
|
-
return {
|
|
17
|
-
isError: true,
|
|
18
|
-
content: [{ type: 'text', text: JSON.stringify(mapDbError(error)) }],
|
|
19
|
-
};
|
|
20
|
-
}
|