@sendinel/mcp-server 1.0.146 → 1.0.147
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 +26 -26
- 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 +73 -74
- 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
|
@@ -13,7 +13,7 @@ function asRecord(value) {
|
|
|
13
13
|
}
|
|
14
14
|
function v1ErrorMessage(value) {
|
|
15
15
|
const body = asRecord(value);
|
|
16
|
-
return asRecord(body.error).
|
|
16
|
+
return dbToolError(asRecord(body.error)).error ?? body.error ?? value;
|
|
17
17
|
}
|
|
18
18
|
async function resolveNameservers(domain) {
|
|
19
19
|
const labels = domain.toLowerCase().split('.').filter(Boolean);
|
|
@@ -81,7 +81,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
81
81
|
.eq('project_id', getProjectId())
|
|
82
82
|
.order('date', { ascending: true });
|
|
83
83
|
if (error)
|
|
84
|
-
return j(
|
|
84
|
+
return j(dbToolError(error));
|
|
85
85
|
return j({ blackout_dates: data ?? [] });
|
|
86
86
|
});
|
|
87
87
|
server.registerTool('add_blackout_date', {
|
|
@@ -111,7 +111,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
111
111
|
.select('id, date, reason, created_at')
|
|
112
112
|
.single();
|
|
113
113
|
if (error)
|
|
114
|
-
return j(
|
|
114
|
+
return j(dbToolError(error));
|
|
115
115
|
return j({ blackout_date: data });
|
|
116
116
|
});
|
|
117
117
|
server.registerTool('remove_blackout_date', {
|
|
@@ -133,7 +133,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
133
133
|
.eq('id', id)
|
|
134
134
|
.eq('project_id', getProjectId());
|
|
135
135
|
if (error)
|
|
136
|
-
return j(
|
|
136
|
+
return j(dbToolError(error));
|
|
137
137
|
if (!count)
|
|
138
138
|
return j({ error: 'Blackout date not found', code: 'NOT_FOUND' });
|
|
139
139
|
return j({ removed: true, id });
|
|
@@ -199,14 +199,14 @@ export function registerDeliveryOpsTools(server) {
|
|
|
199
199
|
.eq('id', domain_id)
|
|
200
200
|
.maybeSingle();
|
|
201
201
|
if (error)
|
|
202
|
-
return j(
|
|
202
|
+
return j(dbToolError(error));
|
|
203
203
|
if (!data)
|
|
204
204
|
return j({ error: 'Domain not found after verification', code: 'NOT_FOUND' });
|
|
205
205
|
return j({ domain_id, checked_live: true, status: data.status, verified_at: data.verified_at,
|
|
206
206
|
readiness: computeDomainReadiness(data) });
|
|
207
207
|
}
|
|
208
208
|
catch (error) {
|
|
209
|
-
return j({ error: error instanceof Error ? error.
|
|
209
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Domain recheck failed' });
|
|
210
210
|
}
|
|
211
211
|
}));
|
|
212
212
|
server.registerTool('dnsbl_check', {
|
|
@@ -229,14 +229,14 @@ export function registerDeliveryOpsTools(server) {
|
|
|
229
229
|
.eq('project_id', projectId)
|
|
230
230
|
.maybeSingle();
|
|
231
231
|
if (siteErr || !site)
|
|
232
|
-
return j({ error: siteErr
|
|
232
|
+
return j({ error: (siteErr ? dbToolError(siteErr).error : undefined) ?? 'Site not found', code: 'NOT_FOUND' });
|
|
233
233
|
const { data, error } = await db().from('dnsbl_snapshots')
|
|
234
234
|
.select('domain, zone, listed, response_code, checked_at')
|
|
235
235
|
.eq('site_id', siteId)
|
|
236
236
|
.order('checked_at', { ascending: false })
|
|
237
237
|
.limit(60);
|
|
238
238
|
if (error)
|
|
239
|
-
return j(
|
|
239
|
+
return j(dbToolError(error));
|
|
240
240
|
const latestByZone = new Map();
|
|
241
241
|
for (const row of data ?? []) {
|
|
242
242
|
if (!latestByZone.has(row.zone))
|
|
@@ -282,7 +282,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
282
282
|
.maybeSingle(),
|
|
283
283
|
]);
|
|
284
284
|
if (siteErr || !site)
|
|
285
|
-
return j({ error: siteErr
|
|
285
|
+
return j({ error: (siteErr ? dbToolError(siteErr).error : undefined) ?? 'Site not found', code: 'NOT_FOUND' });
|
|
286
286
|
const complaintRate = Number(metrics?.complaint_rate ?? 0);
|
|
287
287
|
const draft = [
|
|
288
288
|
`Subject: Delisting request for ${site.sending_domain} from ${zone}`,
|
|
@@ -339,7 +339,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
339
339
|
query = query.eq('job_name', job_name);
|
|
340
340
|
const { data, error } = await query;
|
|
341
341
|
if (error)
|
|
342
|
-
return j(
|
|
342
|
+
return j(dbToolError(error));
|
|
343
343
|
const failed = (data ?? []).filter((row) => row.status === 'failed');
|
|
344
344
|
return j({ runs: data ?? [], recent_failures: failed.length, total: data?.length ?? 0 });
|
|
345
345
|
});
|
|
@@ -365,9 +365,9 @@ export function registerDeliveryOpsTools(server) {
|
|
|
365
365
|
db().from('send_dlq').select('id', { count: 'exact' }).eq('project_id', projectId).eq('resolved', false),
|
|
366
366
|
]);
|
|
367
367
|
if (queueErr)
|
|
368
|
-
return j(
|
|
368
|
+
return j(dbToolError(queueErr));
|
|
369
369
|
if (dlqErr)
|
|
370
|
-
return j(
|
|
370
|
+
return j(dbToolError(dlqErr));
|
|
371
371
|
const counts = {};
|
|
372
372
|
for (const row of queueRows ?? []) {
|
|
373
373
|
counts[row.status] = (counts[row.status] ?? 0) + 1;
|
|
@@ -405,7 +405,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
405
405
|
query = query.eq('resolved', false);
|
|
406
406
|
const { data, error } = await query;
|
|
407
407
|
if (error)
|
|
408
|
-
return j(
|
|
408
|
+
return j(dbToolError(error));
|
|
409
409
|
return j({ dlq: data ?? [], count: data?.length ?? 0 });
|
|
410
410
|
});
|
|
411
411
|
server.registerTool('get_enrollment_dlq', {
|
|
@@ -435,7 +435,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
435
435
|
query = query.eq('resolved', false);
|
|
436
436
|
const { data, error } = await query;
|
|
437
437
|
if (error)
|
|
438
|
-
return j(
|
|
438
|
+
return j(dbToolError(error));
|
|
439
439
|
return j({ dlq: data ?? [], count: data?.length ?? 0 });
|
|
440
440
|
});
|
|
441
441
|
server.registerTool('get_send_queue_rows', {
|
|
@@ -465,7 +465,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
465
465
|
return j({ rows, count: rows.length });
|
|
466
466
|
}
|
|
467
467
|
catch (error) {
|
|
468
|
-
return j({ error: error instanceof Error ? error.
|
|
468
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Queue query failed' });
|
|
469
469
|
}
|
|
470
470
|
});
|
|
471
471
|
server.registerTool('retry_send_queue_rows', {
|
|
@@ -499,7 +499,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
499
499
|
return j({ retried_count: retriedCount });
|
|
500
500
|
}
|
|
501
501
|
catch (error) {
|
|
502
|
-
return j({ error: error instanceof Error ? error.
|
|
502
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Queue retry failed' });
|
|
503
503
|
}
|
|
504
504
|
});
|
|
505
505
|
server.registerTool('cancel_send_queue_rows', {
|
|
@@ -533,7 +533,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
533
533
|
return j({ cancelled_count: cancelledCount });
|
|
534
534
|
}
|
|
535
535
|
catch (error) {
|
|
536
|
-
return j({ error: error instanceof Error ? error.
|
|
536
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Queue cancellation failed' });
|
|
537
537
|
}
|
|
538
538
|
});
|
|
539
539
|
server.registerTool('replay_enrollment_dlq_rows', {
|
|
@@ -575,7 +575,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
575
575
|
});
|
|
576
576
|
}
|
|
577
577
|
catch (error) {
|
|
578
|
-
return j({ error: error instanceof Error ? error.
|
|
578
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Replay preview failed' });
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
581
|
try {
|
|
@@ -599,7 +599,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
599
599
|
return j({ ok: true, replayed_count: replayedCount, skipped_count: skippedCount });
|
|
600
600
|
}
|
|
601
601
|
catch (error) {
|
|
602
|
-
return j({ error: error instanceof Error ? error.
|
|
602
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'Replay failed' });
|
|
603
603
|
}
|
|
604
604
|
}));
|
|
605
605
|
server.registerTool('get_scoring_rules', {
|
|
@@ -619,7 +619,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
619
619
|
.eq('project_id', projectId)
|
|
620
620
|
.maybeSingle();
|
|
621
621
|
if (error)
|
|
622
|
-
return j(
|
|
622
|
+
return j(dbToolError(error));
|
|
623
623
|
return j({ rules: data });
|
|
624
624
|
});
|
|
625
625
|
server.registerTool('update_scoring_rules', {
|
|
@@ -656,7 +656,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
656
656
|
.select('*')
|
|
657
657
|
.single();
|
|
658
658
|
if (error)
|
|
659
|
-
return j(
|
|
659
|
+
return j(dbToolError(error));
|
|
660
660
|
return j({ rules: data, ok: true });
|
|
661
661
|
});
|
|
662
662
|
server.registerTool('explain_contact_score', {
|
|
@@ -688,7 +688,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
688
688
|
db().from('scoring_rules').select('*').eq('project_id', projectId).maybeSingle(),
|
|
689
689
|
]);
|
|
690
690
|
if (contactErr || !contact)
|
|
691
|
-
return j({ error: contactErr
|
|
691
|
+
return j({ error: (contactErr ? dbToolError(contactErr).error : undefined) ?? 'Contact not found', code: 'NOT_FOUND' });
|
|
692
692
|
const effectiveRules = rules ?? {
|
|
693
693
|
click_weight: 5,
|
|
694
694
|
open_weight: 2,
|
|
@@ -782,7 +782,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
782
782
|
.order('id', { ascending: true })
|
|
783
783
|
.range(from, from + size - 1);
|
|
784
784
|
if (error)
|
|
785
|
-
return j(
|
|
785
|
+
return j(dbToolError(error));
|
|
786
786
|
rows.push(...(data ?? []));
|
|
787
787
|
if ((data?.length ?? 0) < size)
|
|
788
788
|
break;
|
|
@@ -817,7 +817,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
817
817
|
.eq('project_id', projectId)
|
|
818
818
|
.maybeSingle();
|
|
819
819
|
if (siteError)
|
|
820
|
-
return j(
|
|
820
|
+
return j(dbToolError(siteError));
|
|
821
821
|
if (!site)
|
|
822
822
|
return j({ error: 'Site not found', code: 'NOT_FOUND' });
|
|
823
823
|
if (!site.domain_id)
|
|
@@ -826,7 +826,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
826
826
|
}
|
|
827
827
|
const { data, error } = await domainQuery;
|
|
828
828
|
if (error)
|
|
829
|
-
return j(
|
|
829
|
+
return j(dbToolError(error));
|
|
830
830
|
if (!data)
|
|
831
831
|
return j({ error: 'Domain query returned no result' });
|
|
832
832
|
return j({ domains: data.map((domain) => ({
|
|
@@ -856,7 +856,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
856
856
|
.eq('project_id', projectId)
|
|
857
857
|
.maybeSingle();
|
|
858
858
|
if (siteErr || !site)
|
|
859
|
-
return j({ error: siteErr
|
|
859
|
+
return j({ error: (siteErr ? dbToolError(siteErr).error : undefined) ?? 'Site not found', code: 'NOT_FOUND' });
|
|
860
860
|
const dnsRecords = [
|
|
861
861
|
{ type: 'TXT', name: domain, value: 'v=spf1 include:amazonses.com ~all' },
|
|
862
862
|
{ type: 'TXT', name: `_dmarc.${domain}`, value: 'v=DMARC1; p=none; rua=mailto:dmarc@sendinel.ai' },
|
|
@@ -866,13 +866,13 @@ export function registerDeliveryOpsTools(server) {
|
|
|
866
866
|
.select('id, name, provider, region, status, dns_records, created_at')
|
|
867
867
|
.single();
|
|
868
868
|
if (error)
|
|
869
|
-
return j(
|
|
869
|
+
return j(dbToolError(error));
|
|
870
870
|
const { error: linkErr } = await db().from('sites')
|
|
871
871
|
.update({ domain_id: created.id, sending_domain: domain, updated_at: new Date().toISOString() })
|
|
872
872
|
.eq('id', site_id)
|
|
873
873
|
.eq('project_id', projectId);
|
|
874
874
|
if (linkErr)
|
|
875
|
-
return j(
|
|
875
|
+
return j(dbToolError(linkErr));
|
|
876
876
|
return j({ ok: true, site_id: site.id, domain: created });
|
|
877
877
|
});
|
|
878
878
|
server.registerTool('get_dmarc_status', {
|
|
@@ -894,7 +894,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
894
894
|
const { data: sites, error: siteError } = await db().schema('email').from('sites')
|
|
895
895
|
.select('id, sending_domain').eq('active', true).eq('project_id', projectId).order('name');
|
|
896
896
|
if (siteError)
|
|
897
|
-
return j(
|
|
897
|
+
return j(dbToolError(siteError));
|
|
898
898
|
const selectedSite = site_id ? (sites ?? []).find((site) => site.id === site_id) : null;
|
|
899
899
|
if (site_id && !selectedSite)
|
|
900
900
|
return j({ error: 'site_id is not a valid site for this project', status: 400 });
|
|
@@ -943,7 +943,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
943
943
|
});
|
|
944
944
|
}
|
|
945
945
|
catch (error) {
|
|
946
|
-
return j({ error: error instanceof Error ? error.
|
|
946
|
+
return j({ error: error instanceof Error ? dbToolError(error).error : 'DMARC status query failed' });
|
|
947
947
|
}
|
|
948
948
|
});
|
|
949
949
|
server.registerTool('list_dmarc_reports', {
|
|
@@ -1082,7 +1082,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
1082
1082
|
.eq('project_id', projectId)
|
|
1083
1083
|
.maybeSingle();
|
|
1084
1084
|
if (siteErr || !site)
|
|
1085
|
-
return j({ error: siteErr
|
|
1085
|
+
return j({ error: (siteErr ? dbToolError(siteErr).error : undefined) ?? 'Site not found', code: 'NOT_FOUND' });
|
|
1086
1086
|
const expiresAt = new Date(Date.now() + ttlHours * 3600 * 1000).toISOString();
|
|
1087
1087
|
const { data: metrics } = await db()
|
|
1088
1088
|
.from('tenant_email_metrics')
|
|
@@ -1101,7 +1101,7 @@ export function registerDeliveryOpsTools(server) {
|
|
|
1101
1101
|
details: { reason, expires_at: expiresAt },
|
|
1102
1102
|
});
|
|
1103
1103
|
if (error)
|
|
1104
|
-
return j(
|
|
1104
|
+
return j(dbToolError(error));
|
|
1105
1105
|
return j({
|
|
1106
1106
|
ok: true,
|
|
1107
1107
|
site_id: siteId,
|
package/build/tools/drafts.js
CHANGED
|
@@ -3,6 +3,7 @@ import { db } from '../db.js';
|
|
|
3
3
|
import { getProjectId } from '../project.js';
|
|
4
4
|
import { checkPlanLimit } from '../plan-limits.js';
|
|
5
5
|
import { recordBrandAssetUsage } from '../lib/brand-asset-usage.js';
|
|
6
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
6
7
|
export function registerDraftTools(server) {
|
|
7
8
|
server.registerTool('create_draft', {
|
|
8
9
|
title: 'Create Draft',
|
|
@@ -34,7 +35,7 @@ export function registerDraftTools(server) {
|
|
|
34
35
|
.select()
|
|
35
36
|
.single();
|
|
36
37
|
if (error)
|
|
37
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
38
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
38
39
|
await recordBrandAssetUsage({
|
|
39
40
|
client: db(),
|
|
40
41
|
projectId: getProjectId(),
|
|
@@ -74,7 +75,7 @@ export function registerDraftTools(server) {
|
|
|
74
75
|
q = q.eq('site_id', site_id);
|
|
75
76
|
const { data, error } = await q;
|
|
76
77
|
if (error)
|
|
77
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
78
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
78
79
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
79
80
|
});
|
|
80
81
|
server.registerTool('get_draft', {
|
|
@@ -97,7 +98,7 @@ export function registerDraftTools(server) {
|
|
|
97
98
|
.eq('project_id', getProjectId())
|
|
98
99
|
.maybeSingle();
|
|
99
100
|
if (error)
|
|
100
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
101
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
101
102
|
if (!data)
|
|
102
103
|
return { content: [{ type: 'text', text: JSON.stringify({ error: 'Draft not found', code: 'NOT_FOUND' }, null, 2) }] };
|
|
103
104
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
@@ -134,7 +135,7 @@ export function registerDraftTools(server) {
|
|
|
134
135
|
.eq('project_id', projectId)
|
|
135
136
|
.maybeSingle();
|
|
136
137
|
if (draftError)
|
|
137
|
-
return { content: [{ type: 'text', text: `Error: ${draftError.
|
|
138
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(draftError).error}` }] };
|
|
138
139
|
if (!draft)
|
|
139
140
|
return { content: [{ type: 'text', text: JSON.stringify({ error: 'Draft not found', code: 'NOT_FOUND' }, null, 2) }] };
|
|
140
141
|
const { data: site, error: siteError } = await db()
|
|
@@ -144,7 +145,7 @@ export function registerDraftTools(server) {
|
|
|
144
145
|
.eq('project_id', projectId)
|
|
145
146
|
.maybeSingle();
|
|
146
147
|
if (siteError)
|
|
147
|
-
return { content: [{ type: 'text', text: `Error: ${siteError.
|
|
148
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(siteError).error}` }] };
|
|
148
149
|
if (!site)
|
|
149
150
|
return { content: [{ type: 'text', text: 'Draft site not found for this project.' }] };
|
|
150
151
|
const insert = {
|
|
@@ -169,7 +170,7 @@ export function registerDraftTools(server) {
|
|
|
169
170
|
.select()
|
|
170
171
|
.single();
|
|
171
172
|
if (campaignError)
|
|
172
|
-
return { content: [{ type: 'text', text: `Failed to create campaign: ${campaignError.
|
|
173
|
+
return { content: [{ type: 'text', text: `Failed to create campaign: ${dbToolError(campaignError).error}` }] };
|
|
173
174
|
const { data: step, error: stepError } = await db()
|
|
174
175
|
.from('campaign_steps')
|
|
175
176
|
.insert({
|
|
@@ -184,7 +185,7 @@ export function registerDraftTools(server) {
|
|
|
184
185
|
.select()
|
|
185
186
|
.single();
|
|
186
187
|
if (stepError)
|
|
187
|
-
return { content: [{ type: 'text', text: `Campaign created (${campaign.id}) but failed to create step: ${stepError.
|
|
188
|
+
return { content: [{ type: 'text', text: `Campaign created (${campaign.id}) but failed to create step: ${dbToolError(stepError).error}` }] };
|
|
188
189
|
await recordBrandAssetUsage({
|
|
189
190
|
client: db(),
|
|
190
191
|
projectId,
|
|
@@ -231,7 +232,7 @@ export function registerDraftTools(server) {
|
|
|
231
232
|
.select()
|
|
232
233
|
.single();
|
|
233
234
|
if (error)
|
|
234
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
235
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
235
236
|
if (!data)
|
|
236
237
|
return { content: [{ type: 'text', text: 'Draft not found or not in pending status.' }] };
|
|
237
238
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
@@ -259,7 +260,7 @@ export function registerDraftTools(server) {
|
|
|
259
260
|
.select()
|
|
260
261
|
.single();
|
|
261
262
|
if (error)
|
|
262
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
263
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
263
264
|
if (!data)
|
|
264
265
|
return { content: [{ type: 'text', text: 'Draft not found or not in pending status.' }] };
|
|
265
266
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
package/build/tools/forms.js
CHANGED
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { db } from '../db.js';
|
|
3
3
|
import { getProjectId } from '../project.js';
|
|
4
4
|
import { assertHttpUrl } from '../lib/url-validation.js';
|
|
5
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
5
6
|
const j = (data) => ({ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] });
|
|
6
7
|
export function registerFormTools(server) {
|
|
7
8
|
server.registerTool('list_forms', {
|
|
@@ -20,7 +21,7 @@ export function registerFormTools(server) {
|
|
|
20
21
|
query = query.eq('site_id', site_id);
|
|
21
22
|
const { data, error } = await query;
|
|
22
23
|
if (error)
|
|
23
|
-
return j(
|
|
24
|
+
return j(dbToolError(error));
|
|
24
25
|
return j({ forms: (data ?? []).map((form) => ({ ...form, field_count: Array.isArray(form.fields) ? form.fields.length : 0, submission_count: form.submissions ?? 0 })) });
|
|
25
26
|
});
|
|
26
27
|
server.registerTool('get_form_stats', {
|
|
@@ -37,7 +38,7 @@ export function registerFormTools(server) {
|
|
|
37
38
|
const projectId = getProjectId();
|
|
38
39
|
const { data: form, error: formError } = await db().from('signup_forms').select('id, submissions').eq('id', form_id).eq('project_id', projectId).maybeSingle();
|
|
39
40
|
if (formError)
|
|
40
|
-
return j(
|
|
41
|
+
return j(dbToolError(formError));
|
|
41
42
|
if (!form)
|
|
42
43
|
return j({ error: 'Form not found', code: 'NOT_FOUND' });
|
|
43
44
|
const since = new Date(Date.now() - 7 * 86400000).toISOString();
|
|
@@ -48,7 +49,7 @@ export function registerFormTools(server) {
|
|
|
48
49
|
.eq('metadata->>form_id', form_id)
|
|
49
50
|
.gte('created_at', since);
|
|
50
51
|
if (eventsError)
|
|
51
|
-
return j(
|
|
52
|
+
return j(dbToolError(eventsError));
|
|
52
53
|
return j({ submissions_total: form.submissions ?? 0, submissions_7d: sevenDay ?? 0, top_sources: [], conversion_rate: null });
|
|
53
54
|
});
|
|
54
55
|
server.registerTool('create_form', {
|
|
@@ -101,7 +102,7 @@ export function registerFormTools(server) {
|
|
|
101
102
|
if (error?.code === '23505')
|
|
102
103
|
return j({ error: 'slug already exists', code: 'conflict' });
|
|
103
104
|
if (error)
|
|
104
|
-
return j({ error
|
|
105
|
+
return j({ ...dbToolError(error), code: 'INTERNAL_ERROR' });
|
|
105
106
|
return j({ form: data });
|
|
106
107
|
});
|
|
107
108
|
}
|
package/build/tools/gdpr.js
CHANGED
|
@@ -3,6 +3,7 @@ import { db } from '../db.js';
|
|
|
3
3
|
import { getProjectId } from '../project.js';
|
|
4
4
|
import { logAudit } from '../audit.js';
|
|
5
5
|
import { assertChannelCanExecute, currentChannelContext, requestApproval, checkApproval } from '../lib/action-approvals.js';
|
|
6
|
+
import { dbToolError } from '../lib/db-error.js';
|
|
6
7
|
const txt = (text) => ({ content: [{ type: 'text', text }] });
|
|
7
8
|
const j = (data) => txt(JSON.stringify(data, null, 2));
|
|
8
9
|
export function registerGdprTools(server) {
|
|
@@ -51,7 +52,7 @@ export function registerGdprTools(server) {
|
|
|
51
52
|
p_deleted_by: 'mcp',
|
|
52
53
|
});
|
|
53
54
|
if (error)
|
|
54
|
-
return txt(`Error: ${error.
|
|
55
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
55
56
|
logAudit({ action: 'contact.gdpr_deleted', resourceType: 'contact', resourceId: contact_id, details: { reason, result: data } });
|
|
56
57
|
return j(data);
|
|
57
58
|
});
|
|
@@ -74,7 +75,7 @@ export function registerGdprTools(server) {
|
|
|
74
75
|
.order('created_at', { ascending: false })
|
|
75
76
|
.limit(limit ?? 50);
|
|
76
77
|
if (error)
|
|
77
|
-
return { content: [{ type: 'text', text: `Error: ${error.
|
|
78
|
+
return { content: [{ type: 'text', text: `Error: ${dbToolError(error).error}` }] };
|
|
78
79
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
79
80
|
});
|
|
80
81
|
server.registerTool('list_ccpa_requests', {
|
|
@@ -108,7 +109,7 @@ export function registerGdprTools(server) {
|
|
|
108
109
|
.order('created_at', { ascending: false })
|
|
109
110
|
.limit(limit ?? 50);
|
|
110
111
|
if (error)
|
|
111
|
-
return txt(`Error: ${error.
|
|
112
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
112
113
|
return j({ requests: data ?? [], count: data?.length ?? 0 });
|
|
113
114
|
});
|
|
114
115
|
server.registerTool('action_ccpa_request', {
|
|
@@ -135,7 +136,7 @@ export function registerGdprTools(server) {
|
|
|
135
136
|
.eq('regulation', 'ccpa')
|
|
136
137
|
.maybeSingle();
|
|
137
138
|
if (fetchErr)
|
|
138
|
-
return txt(`Error: ${fetchErr.
|
|
139
|
+
return txt(`Error: ${dbToolError(fetchErr).error}`);
|
|
139
140
|
if (!request)
|
|
140
141
|
return txt('CCPA request not found or does not belong to this project.');
|
|
141
142
|
if (request.status === 'completed' || request.status === 'expired') {
|
|
@@ -160,7 +161,7 @@ export function registerGdprTools(server) {
|
|
|
160
161
|
.select('id, email, status, verified_at')
|
|
161
162
|
.maybeSingle();
|
|
162
163
|
if (error)
|
|
163
|
-
return txt(`Error: ${error.
|
|
164
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
164
165
|
if (!data)
|
|
165
166
|
return txt('Request changed or expired before it could be verified. Refresh the queue and try again.');
|
|
166
167
|
void logAudit({
|
|
@@ -203,7 +204,7 @@ export function registerGdprTools(server) {
|
|
|
203
204
|
.eq('email', request.email)
|
|
204
205
|
.maybeSingle();
|
|
205
206
|
if (contactErr)
|
|
206
|
-
return txt(`Error: ${contactErr.
|
|
207
|
+
return txt(`Error: ${dbToolError(contactErr).error}`);
|
|
207
208
|
if (contact) {
|
|
208
209
|
const { error: deleteErr } = await db().rpc('delete_contact_gdpr', {
|
|
209
210
|
p_contact_id: contact.id,
|
|
@@ -211,7 +212,7 @@ export function registerGdprTools(server) {
|
|
|
211
212
|
p_deleted_by: 'mcp_ccpa_action',
|
|
212
213
|
});
|
|
213
214
|
if (deleteErr)
|
|
214
|
-
return txt(`Deletion failed: ${deleteErr.
|
|
215
|
+
return txt(`Deletion failed: ${dbToolError(deleteErr).error}`);
|
|
215
216
|
}
|
|
216
217
|
const completedAt = new Date().toISOString();
|
|
217
218
|
const { data, error } = await db()
|
|
@@ -229,7 +230,7 @@ export function registerGdprTools(server) {
|
|
|
229
230
|
.select('id, email, status, completed_at')
|
|
230
231
|
.maybeSingle();
|
|
231
232
|
if (error)
|
|
232
|
-
return txt(`Error: ${error.
|
|
233
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
233
234
|
if (!data)
|
|
234
235
|
return txt('Request changed before it could be completed. Refresh the queue before retrying.');
|
|
235
236
|
void logAudit({
|
|
@@ -251,7 +252,7 @@ export function registerGdprTools(server) {
|
|
|
251
252
|
.select('id, email, status, completed_at')
|
|
252
253
|
.maybeSingle();
|
|
253
254
|
if (error)
|
|
254
|
-
return txt(`Error: ${error.
|
|
255
|
+
return txt(`Error: ${dbToolError(error).error}`);
|
|
255
256
|
if (!data)
|
|
256
257
|
return txt('Request changed before it could be completed. Refresh the queue before retrying.');
|
|
257
258
|
void logAudit({
|
|
@@ -74,7 +74,7 @@ export function registerLandingPageTools(server) {
|
|
|
74
74
|
query = query.eq('site_id', site_id);
|
|
75
75
|
const { data, error } = await query.order('created_at', { ascending: false });
|
|
76
76
|
if (error)
|
|
77
|
-
return j(
|
|
77
|
+
return j(dbToolError(error));
|
|
78
78
|
return j({ pages: data ?? [] });
|
|
79
79
|
});
|
|
80
80
|
server.registerTool('get_landing_page', {
|
|
@@ -90,7 +90,7 @@ export function registerLandingPageTools(server) {
|
|
|
90
90
|
.eq('id', page_id)
|
|
91
91
|
.maybeSingle();
|
|
92
92
|
if (error)
|
|
93
|
-
return j(
|
|
93
|
+
return j(dbToolError(error));
|
|
94
94
|
if (!data)
|
|
95
95
|
return j({ error: 'Landing page not found', code: 'NOT_FOUND' });
|
|
96
96
|
return j(data);
|
|
@@ -274,7 +274,7 @@ export function registerLandingPageTools(server) {
|
|
|
274
274
|
.eq('id', page_id)
|
|
275
275
|
.eq('project_id', projectId);
|
|
276
276
|
if (error)
|
|
277
|
-
return j(
|
|
277
|
+
return j(dbToolError(error));
|
|
278
278
|
await logAudit({ action: 'landing_page.deleted', resourceType: 'landing_page', resourceId: page_id });
|
|
279
279
|
return j({ deleted: true });
|
|
280
280
|
}));
|