@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.
Files changed (42) hide show
  1. package/build/lib/action-approvals.js +3 -1
  2. package/build/lib/db-error.d.ts +39 -6
  3. package/build/lib/db-error.js +221 -11
  4. package/build/server.js +5 -3
  5. package/build/tools/ab-testing.js +4 -3
  6. package/build/tools/advisor.js +2 -2
  7. package/build/tools/ai-work.js +14 -13
  8. package/build/tools/analytics.js +33 -32
  9. package/build/tools/approvals.js +3 -1
  10. package/build/tools/automations.js +12 -11
  11. package/build/tools/brand-kit.js +4 -3
  12. package/build/tools/campaign-sets.js +11 -10
  13. package/build/tools/campaigns.js +81 -79
  14. package/build/tools/compound.js +7 -6
  15. package/build/tools/connector.js +4 -3
  16. package/build/tools/contacts.js +44 -45
  17. package/build/tools/content.js +16 -16
  18. package/build/tools/convert-imported.js +8 -7
  19. package/build/tools/data-proposals.js +8 -7
  20. package/build/tools/data.js +39 -38
  21. package/build/tools/delivery-ops.js +32 -32
  22. package/build/tools/drafts.js +10 -9
  23. package/build/tools/forms.js +5 -4
  24. package/build/tools/gdpr.js +10 -9
  25. package/build/tools/landing-pages.js +3 -3
  26. package/build/tools/lists.js +21 -20
  27. package/build/tools/newsletters.js +26 -25
  28. package/build/tools/org.js +26 -23
  29. package/build/tools/segments.js +4 -5
  30. package/build/tools/short-links.js +7 -6
  31. package/build/tools/signatures.js +10 -9
  32. package/build/tools/sites.js +9 -8
  33. package/build/tools/sms.js +14 -13
  34. package/build/tools/social-posts.js +20 -19
  35. package/build/tools/social-replies.js +7 -6
  36. package/build/tools/surveys.js +10 -9
  37. package/build/tools/templates.js +14 -13
  38. package/build/tools/warmup.js +5 -4
  39. package/build/tools/webhooks.js +5 -4
  40. package/package.json +1 -1
  41. package/build/lib/db-errors.d.ts +0 -12
  42. 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).message ?? body.error ?? value;
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({ error: error.message });
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({ error: error.message });
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({ error: error.message });
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({ error: error.message });
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.message : 'Domain recheck failed' });
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?.message ?? 'Site not found', code: 'NOT_FOUND' });
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({ error: error.message });
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?.message ?? 'Site not found', code: 'NOT_FOUND' });
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({ error: error.message });
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({ error: queueErr.message });
368
+ return j(dbToolError(queueErr));
369
369
  if (dlqErr)
370
- return j({ error: dlqErr.message });
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({ error: error.message });
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({ error: error.message });
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.message : 'Queue query failed' });
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.message : 'Queue retry failed' });
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.message : 'Queue cancellation failed' });
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.message : 'Replay preview failed' });
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.message : 'Replay failed' });
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({ error: error.message });
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({ error: error.message });
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?.message ?? 'Contact not found', code: 'NOT_FOUND' });
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({ error: error.message });
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({ error: siteError.message });
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({ error: error.message });
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?.message ?? 'Site not found', code: 'NOT_FOUND' });
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({ error: error.message });
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({ error: linkErr.message });
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({ error: siteError.message });
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.message : 'DMARC status query failed' });
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?.message ?? 'Site not found', code: 'NOT_FOUND' });
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({ error: error.message });
1104
+ return j(dbToolError(error));
1105
1105
  return j({
1106
1106
  ok: true,
1107
1107
  site_id: siteId,
@@ -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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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.message}` }] };
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) }] };
@@ -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({ error: error.message });
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({ error: formError.message });
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({ error: eventsError.message });
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: error.message, code: 'INTERNAL_ERROR' });
105
+ return j({ ...dbToolError(error), code: 'INTERNAL_ERROR' });
105
106
  return j({ form: data });
106
107
  });
107
108
  }
@@ -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.message}`);
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.message}` }] };
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.message}`);
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.message}`);
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.message}`);
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.message}`);
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.message}`);
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.message}`);
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.message}`);
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({ error: error.message });
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({ error: error.message });
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({ error: error.message });
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
  }));