Discord
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
version: 3
|
||||
name: email_db
|
||||
label: Email Database
|
||||
description: Query, search, aggregate, and manage the user's email database. Use this tool to answer questions about emails, find messages by sender/domain/date/content, get statistics, and delete emails. The database is a local SQLite copy of the user's synced Gmail inbox.
|
||||
description: Query, search, aggregate, and manage the user's email database. Use this tool to answer questions about emails, find messages by sender/domain/date/content, get statistics, and delete emails. The database is a local SQLite copy of the user's synced Gmail. All actions default to inbox scope — use folder parameter to query other folders like sent, spam, trash, or 'all' for everything.
|
||||
language: typescript
|
||||
inputs:
|
||||
action:
|
||||
@@ -36,6 +36,10 @@ inputs:
|
||||
type: string
|
||||
description: "Attachment content type filter. Full MIME type (e.g. 'image/jpeg') or just the type prefix (e.g. 'image' matches all image types). Used with 'attachments' action."
|
||||
optional: true
|
||||
folder:
|
||||
type: string
|
||||
description: "Gmail folder/label to scope results. Defaults to 'inbox'. Use 'all' for all folders. Common values: inbox, sent, spam, trash."
|
||||
optional: true
|
||||
limit:
|
||||
type: number
|
||||
description: "Maximum number of results to return (default 20, max 100)."
|
||||
@@ -65,9 +69,9 @@ emails (
|
||||
id TEXT PRIMARY KEY,
|
||||
integration TEXT, -- source: 'gmail', 'outlook', etc.
|
||||
email_account TEXT, -- which account: 'user@gmail.com'
|
||||
from_name TEXT,
|
||||
from_address TEXT,
|
||||
from_domain TEXT,
|
||||
from_name TEXT, -- sender display name
|
||||
from_address TEXT, -- sender email (lowercase)
|
||||
from_domain TEXT, -- domain extracted from sender
|
||||
to_address TEXT,
|
||||
cc TEXT,
|
||||
subject TEXT,
|
||||
@@ -77,7 +81,8 @@ emails (
|
||||
text_body TEXT,
|
||||
attachment_count INTEGER,
|
||||
read INTEGER,
|
||||
deleted INTEGER
|
||||
deleted INTEGER,
|
||||
labels TEXT -- comma-separated label list (e.g. 'INBOX,UNREAD,CATEGORY_UPDATES')
|
||||
)
|
||||
|
||||
attachments (
|
||||
@@ -91,10 +96,13 @@ attachments (
|
||||
|
||||
## Examples
|
||||
|
||||
- Search for invoices: `action: "search", search: "invoice"`
|
||||
- Search for invoices (inbox only): `action: "search", search: "invoice"`
|
||||
- Search sent emails for invoices: `action: "search", search: "invoice", folder: "sent"`
|
||||
- Search all folders: `action: "search", search: "invoice", folder: "all"`
|
||||
- Count emails from a domain: `action: "count", domain: "newsletter.com"`
|
||||
- Delete all emails from a domain: `action: "delete", domain: "spam.com"`
|
||||
- Top 10 domains: `action: "domains", limit: 10`
|
||||
- Stats for spam folder: `action: "stats", folder: "spam"`
|
||||
- List attachment types: `action: "attachment-types"`
|
||||
- Find image attachments: `action: "attachments", content_type: "image"`
|
||||
- Find PDFs from a sender: `action: "attachments", content_type: "application/pdf", sender: "boss@company.com"`
|
||||
|
||||
@@ -15,6 +15,7 @@ type Params = {
|
||||
before?: string;
|
||||
after?: string;
|
||||
content_type?: string;
|
||||
folder?: string;
|
||||
limit?: number;
|
||||
};
|
||||
|
||||
@@ -72,9 +73,18 @@ function formatRows(rows: Record<string, unknown>[], limit: number): string {
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
function buildWhereClause(params: Params): string {
|
||||
function buildFolderCondition(folder: string | undefined): string | null {
|
||||
if (!folder || folder === 'all') return null;
|
||||
return `labels LIKE ${sqlStr(`%${folder}%`)}`;
|
||||
}
|
||||
|
||||
function buildWhereClause(params: Params, defaultFolder?: string): string {
|
||||
const conditions: string[] = [];
|
||||
|
||||
const folder = params.folder ?? defaultFolder;
|
||||
const folderCond = buildFolderCondition(folder);
|
||||
if (folderCond) conditions.push(folderCond);
|
||||
|
||||
if (params.domain) {
|
||||
conditions.push(`from_domain = ${sqlStr(params.domain.toLowerCase())}`);
|
||||
}
|
||||
@@ -111,20 +121,21 @@ function runQuery(sql: string, limit: number): string {
|
||||
}
|
||||
|
||||
function searchEmails(params: Params, limit: number): string {
|
||||
const where = withActive(buildWhereClause(params));
|
||||
const where = withActive(buildWhereClause(params, 'inbox'));
|
||||
const rows = queryJson(`SELECT id, from_name, from_address, subject, date, snippet, attachment_count FROM emails ${where} ORDER BY date DESC LIMIT ${limit}`);
|
||||
return formatRows(rows, limit);
|
||||
}
|
||||
|
||||
function getStats(): string {
|
||||
const totalRows = queryJson('SELECT COUNT(*) as count FROM emails WHERE deleted = 0');
|
||||
function getStats(params: Params): string {
|
||||
const folderWhere = withActive(buildWhereClause({ action: 'stats', folder: params.folder }, 'inbox'));
|
||||
const totalRows = queryJson(`SELECT COUNT(*) as count FROM emails ${folderWhere}`);
|
||||
const total = (totalRows[0]?.count as number) ?? 0;
|
||||
if (total === 0) return 'Email database is empty.';
|
||||
|
||||
const dateRange = queryJson('SELECT MIN(date) as oldest, MAX(date) as newest FROM emails WHERE deleted = 0');
|
||||
const topDomains = queryJson('SELECT from_domain, COUNT(*) as count FROM emails WHERE deleted = 0 GROUP BY from_domain ORDER BY count DESC LIMIT 10');
|
||||
const topSenders = queryJson('SELECT from_address, from_name, COUNT(*) as count FROM emails WHERE deleted = 0 GROUP BY from_address ORDER BY count DESC LIMIT 10');
|
||||
const attRows = queryJson('SELECT COUNT(*) as count FROM emails WHERE deleted = 0 AND attachment_count > 0');
|
||||
const dateRange = queryJson(`SELECT MIN(date) as oldest, MAX(date) as newest FROM emails ${folderWhere}`);
|
||||
const topDomains = queryJson(`SELECT from_domain, COUNT(*) as count FROM emails ${folderWhere} GROUP BY from_domain ORDER BY count DESC LIMIT 10`);
|
||||
const topSenders = queryJson(`SELECT from_address, from_name, COUNT(*) as count FROM emails ${folderWhere} GROUP BY from_address ORDER BY count DESC LIMIT 10`);
|
||||
const attRows = queryJson(`SELECT COUNT(*) as count FROM emails ${folderWhere} AND attachment_count > 0`);
|
||||
const withAttachments = (attRows[0]?.count as number) ?? 0;
|
||||
|
||||
const dr = dateRange[0] ?? {};
|
||||
@@ -146,7 +157,7 @@ function getStats(): string {
|
||||
}
|
||||
|
||||
function countEmails(params: Params): string {
|
||||
const where = withActive(buildWhereClause(params));
|
||||
const where = withActive(buildWhereClause(params, 'inbox'));
|
||||
const rows = queryJson(`SELECT COUNT(*) as count FROM emails ${where}`);
|
||||
const count = (rows[0]?.count as number) ?? 0;
|
||||
|
||||
@@ -161,16 +172,18 @@ function countEmails(params: Params): string {
|
||||
return `${count} email${count !== 1 ? 's' : ''}${desc}.`;
|
||||
}
|
||||
|
||||
function listDomains(limit: number): string {
|
||||
const rows = queryJson(`SELECT from_domain, COUNT(*) as count FROM emails WHERE deleted = 0 GROUP BY from_domain ORDER BY count DESC LIMIT ${limit}`);
|
||||
function listDomains(params: Params, limit: number): string {
|
||||
const where = withActive(buildWhereClause({ action: 'domains', folder: params.folder }, 'inbox'));
|
||||
const rows = queryJson(`SELECT from_domain, COUNT(*) as count FROM emails ${where} GROUP BY from_domain ORDER BY count DESC LIMIT ${limit}`);
|
||||
if (rows.length === 0) return 'No emails in database.';
|
||||
|
||||
const lines = rows.map((d, i) => `${i + 1}. ${d.from_domain} — ${d.count} email${(d.count as number) !== 1 ? 's' : ''}`);
|
||||
return [`**Sender Domains** (${rows.length}):`, '', ...lines].join('\n');
|
||||
}
|
||||
|
||||
function listSenders(limit: number): string {
|
||||
const rows = queryJson(`SELECT from_address, from_name, COUNT(*) as count FROM emails WHERE deleted = 0 GROUP BY from_address ORDER BY count DESC LIMIT ${limit}`);
|
||||
function listSenders(params: Params, limit: number): string {
|
||||
const where = withActive(buildWhereClause({ action: 'senders', folder: params.folder }, 'inbox'));
|
||||
const rows = queryJson(`SELECT from_address, from_name, COUNT(*) as count FROM emails ${where} GROUP BY from_address ORDER BY count DESC LIMIT ${limit}`);
|
||||
if (rows.length === 0) return 'No emails in database.';
|
||||
|
||||
const lines = rows.map((s, i) => {
|
||||
@@ -217,7 +230,7 @@ function searchAttachments(params: Params, limit: number): string {
|
||||
}
|
||||
|
||||
function deleteEmails(params: Params): string {
|
||||
const where = buildWhereClause(params);
|
||||
const where = buildWhereClause(params, 'inbox');
|
||||
if (!where) {
|
||||
throw new Error('Delete requires at least one filter (domain, sender, before, after, or search).');
|
||||
}
|
||||
@@ -254,16 +267,16 @@ export async function execute(_toolCallId: string, params: Params): Promise<Tool
|
||||
return ok(searchEmails(params, limit));
|
||||
|
||||
case 'stats':
|
||||
return ok(getStats());
|
||||
return ok(getStats(params));
|
||||
|
||||
case 'count':
|
||||
return ok(countEmails(params));
|
||||
|
||||
case 'domains':
|
||||
return ok(listDomains(limit));
|
||||
return ok(listDomains(params, limit));
|
||||
|
||||
case 'senders':
|
||||
return ok(listSenders(limit));
|
||||
return ok(listSenders(params, limit));
|
||||
|
||||
case 'attachment-types':
|
||||
return ok(listAttachmentTypes(limit));
|
||||
|
||||
Reference in New Issue
Block a user