@sendinel/mcp-server 1.0.23 → 1.0.24
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/tools/analytics.js +30 -8
- package/package.json +1 -1
package/build/tools/analytics.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fetchAllRows } from '@sendinel/domain';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { db, adminDb } from '../db.js';
|
|
3
4
|
import { getProjectId } from '../project.js';
|
|
@@ -431,42 +432,54 @@ export function registerAnalyticsTools(server) {
|
|
|
431
432
|
if (siteError)
|
|
432
433
|
return { content: [{ type: 'text', text: `Error: ${siteError.message}` }] };
|
|
433
434
|
const trackingEnabled = site?.tracking_enabled ?? false;
|
|
434
|
-
const { data, error } = await db()
|
|
435
|
+
const { data, error, pageCount: logPageCount } = await fetchAllRows(async (offset, limit) => db()
|
|
435
436
|
.from('email_log')
|
|
436
437
|
.select('id, provider_message_id, status, sent_at')
|
|
437
438
|
.eq('project_id', projectId)
|
|
438
439
|
.eq('site_id', site_id)
|
|
439
440
|
.gte('sent_at', `${start_date}T00:00:00Z`)
|
|
440
|
-
.lte('sent_at', `${end}T23:59:59Z`)
|
|
441
|
+
.lte('sent_at', `${end}T23:59:59Z`)
|
|
442
|
+
.order('id', { ascending: true })
|
|
443
|
+
.range(offset, offset + limit - 1));
|
|
441
444
|
if (error)
|
|
442
445
|
return { content: [{ type: 'text', text: `Error: ${error.message}` }] };
|
|
443
446
|
const rows = (data ?? []);
|
|
444
447
|
let eventCounts = { opened: 0, clicked: 0 };
|
|
448
|
+
let eventRows = [];
|
|
449
|
+
let eventPageCount = 0;
|
|
445
450
|
if (trackingEnabled) {
|
|
446
|
-
const { data: events, error: eventsError } = await db()
|
|
451
|
+
const { data: events, error: eventsError, pageCount } = await fetchAllRows(async (offset, limit) => db()
|
|
447
452
|
.from('email_events')
|
|
448
453
|
.select('provider_message_id, event_type')
|
|
449
454
|
.eq('project_id', projectId)
|
|
450
455
|
.eq('site_id', site_id)
|
|
451
456
|
.in('event_type', ['open', 'click'])
|
|
452
457
|
.gte('occurred_at', `${start_date}T00:00:00Z`)
|
|
453
|
-
.lte('occurred_at', `${end}T23:59:59Z`)
|
|
458
|
+
.lte('occurred_at', `${end}T23:59:59Z`)
|
|
459
|
+
.order('id', { ascending: true })
|
|
460
|
+
.range(offset, offset + limit - 1));
|
|
454
461
|
if (eventsError)
|
|
455
462
|
return { content: [{ type: 'text', text: `Error: ${eventsError.message}` }] };
|
|
463
|
+
eventRows = events ?? [];
|
|
464
|
+
eventPageCount = pageCount;
|
|
456
465
|
const messageIds = new Set(rows.map(row => row.provider_message_id).filter((id) => Boolean(id)));
|
|
457
466
|
eventCounts = {
|
|
458
|
-
opened: distinctEventCount(
|
|
459
|
-
clicked: distinctEventCount(
|
|
467
|
+
opened: distinctEventCount(eventRows, 'open', messageIds),
|
|
468
|
+
clicked: distinctEventCount(eventRows, 'click', messageIds),
|
|
460
469
|
};
|
|
461
470
|
}
|
|
462
471
|
const metrics = computeAnalyticsMetrics(rows, trackingEnabled ? eventCounts : undefined);
|
|
463
472
|
const periodDays = daysBetween(start_date, end);
|
|
464
473
|
const priorStart = dateOnly(addDays(new Date(`${start_date}T00:00:00Z`), -periodDays));
|
|
465
474
|
const priorEnd = dateOnly(addDays(new Date(`${start_date}T00:00:00Z`), -1));
|
|
466
|
-
const { data: subscribers } = await db()
|
|
475
|
+
const { data: subscribers, error: subscribersError, pageCount: subscriberPageCount } = await fetchAllRows(async (offset, limit) => db()
|
|
467
476
|
.from('subscribers')
|
|
468
477
|
.select('created_at, last_engaged_at, last_opened_at, last_clicked_at')
|
|
469
|
-
.eq('project_id', projectId)
|
|
478
|
+
.eq('project_id', projectId)
|
|
479
|
+
.order('id', { ascending: true })
|
|
480
|
+
.range(offset, offset + limit - 1));
|
|
481
|
+
if (subscribersError)
|
|
482
|
+
return { content: [{ type: 'text', text: `Error: ${subscribersError.message}` }] };
|
|
470
483
|
const subscriberRows = (subscribers ?? []);
|
|
471
484
|
const currentNew = subscriberRows.filter(s => s.created_at && s.created_at >= `${start_date}T00:00:00Z` && s.created_at <= `${end}T23:59:59Z`).length;
|
|
472
485
|
const priorNew = subscriberRows.filter(s => s.created_at && s.created_at >= `${priorStart}T00:00:00Z` && s.created_at <= `${priorEnd}T23:59:59Z`).length;
|
|
@@ -488,6 +501,15 @@ export function registerAnalyticsTools(server) {
|
|
|
488
501
|
list_growth_rate: subscriberRows.length > 0 ? pctNumber(currentNew / subscriberRows.length) : 0,
|
|
489
502
|
inactive_pct: subscriberRows.length > 0 ? pctNumber(inactive / subscriberRows.length) : 0,
|
|
490
503
|
sender_health_score: computeSenderHealthScore(metrics, trackingEnabled),
|
|
504
|
+
coverage: {
|
|
505
|
+
log_rows_scanned: rows.length,
|
|
506
|
+
log_page_count: logPageCount,
|
|
507
|
+
event_rows_scanned: trackingEnabled ? eventRows.length : null,
|
|
508
|
+
event_page_count: trackingEnabled ? eventPageCount : null,
|
|
509
|
+
subscriber_rows_scanned: subscriberRows.length,
|
|
510
|
+
subscriber_page_count: subscriberPageCount,
|
|
511
|
+
complete: true,
|
|
512
|
+
},
|
|
491
513
|
};
|
|
492
514
|
if (trackingEnabled) {
|
|
493
515
|
response.open_rate = metrics.sent > 0 ? percent(metrics.open_rate) : 'N/A';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendinel/mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.24",
|
|
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",
|