Creates event-driven email listeners that monitor for specific conditions (like urgent emails from boss, newsletters to archive, package tracking) and execute custom actions. Use when user wants to be notified about emails, automatically handle certain emails, or set up email automation workflows.
npx skills add https://github.com/anthropics/claude-agent-sdk-demos --skill listener-creator
Creates TypeScript listener files that monitor email events and execute custom logic when conditions are met.
Use this skill when the user wants to:
Listeners are TypeScript files in agent/custom_scripts/listeners/ that:
config object defining the event type and metadatahandler function that filters and processes eventsListenerContext methods to perform actions (notify, archive, star, etc.)The system automatically loads enabled listeners and executes them when matching events occur.
Parse the user's request to identify:
// Available event types:
- "email_received" // Most common - new email arrives
- "email_sent" // User sends an email
- "email_starred" // Email is starred
- "email_archived" // Email is archived
- "email_labeled" // Label added to email
- "scheduled_time" // Time-based (cron) - requires scheduler setup
Create a file in agent/custom_scripts/listeners/ with this structure:
import type { ListenerConfig, Email, ListenerContext } from "../types";
export const config: ListenerConfig = {
id: "unique_listener_id", // kebab-case, descriptive
name: "Human Readable Name", // For UI display
description: "What this does", // Optional but helpful
enabled: true, // Start enabled
event: "email_received" // Event type
};
export async function handler(email: Email, context: ListenerContext): Promise<void> {
// 1. Basic filter (identity/sender only)
if (!email.from.includes("[email protected]")) return;
// 2. Use AI for intelligent classification (PREFERRED over keyword matching)
const analysis = await context.callAgent<{ isUrgent: boolean; reason: string }>({
prompt: `Is this email urgent?\nSubject: ${email.subject}\nBody: ${email.body.substring(0, 500)}`,
schema: {
type: "object",
properties: {
isUrgent: { type: "boolean" },
reason: { type: "string" }
},
required: ["isUrgent", "reason"]
},
model: "haiku"
});
if (!analysis.isUrgent) return;
// 3. Perform actions via context methods
await context.notify(`Urgent email: ${email.subject}\n${analysis.reason}`, {
priority: "high"
});
await context.starEmail(email.messageId);
}
Use kebab-case matching the listener's purpose:
boss-urgent-watcher.tsauto-archive-newsletters.tspackage-tracking.tsdaily-summary.tsThe ListenerContext provides these methods:
// Notifications
await context.notify(message, { priority: "high" | "normal" | "low" });
// Email actions
await context.archiveEmail(emailId);
await context.starEmail(emailId);
await context.unstarEmail(emailId);
await context.markAsRead(emailId);
await context.markAsUnread(emailId);
await context.addLabel(emailId, "label-name");
await context.removeLabel(emailId, "label-name");
// AI-powered analysis
const result = await context.callAgent<ResultType>({
prompt: "Your prompt with email content",
schema: {
type: "object",
properties: { field: { type: "string" } },
required: ["field"]
},
model: "haiku" // or "sonnet" or "opus"
});
Default to using context.callAgent() for intelligent decision-making instead of hard-coded keyword lists. This provides better accuracy and adaptability.
// PREFERRED: AI-based urgency detection
const analysis = await context.callAgent<{ isUrgent: boolean; reason: string }>({
prompt: `Analyze if this email is urgent:
Subject: ${email.subject}
Body: ${email.body.substring(0, 500)}
Is this email urgent or time-sensitive? Consider context, not just keywords.`,
schema: {
type: "object",
properties: {
isUrgent: { type: "boolean" },
reason: { type: "string" }
},
required: ["isUrgent", "reason"]
},
model: "haiku" // Fast and cost-effective
});
if (analysis.isUrgent) {
await context.notify(`Urgent: ${email.subject}\n${analysis.reason}`);
}
// AVOID: Hard-coded keyword lists (brittle and prone to false positives)
// const isUrgent = subject.includes("urgent") || subject.includes("asap");
Reference the template files for common patterns:
context.callAgent() instead of hard-coded keyword lists for intelligent decision-makingAlways import types from the correct location:
import type { ListenerConfig, Email, ListenerContext } from "../types";
// For scheduled listeners:
import type { ListenerConfig, ListenerContext } from "../types";
// For labeled event:
import type { ListenerConfig, Email, ListenerContext } from "../types";
Basic filter (sender/type) → Call AI agent for intelligent classification → Act on AI result → Notify if important
This is the recommended approach for most listeners as it:
Basic filter (sender only) → Notify → Optional star/label
Only use this when: The trigger is purely identity-based (e.g., "notify me about ALL emails from X")
Basic filter → Archive → Mark as read → Optional notify
Run at specific time → Query emails → Analyze → Send summary
When the user requests a listener:
email_received)agent/custom_scripts/listeners/[listener:filename.ts] notation (e.g., [listener:boss-urgent-watcher.ts]) for easy parsing and linking in the UIWhen presenting a created listener to the user, use this format:
Created listener: [listener:boss-urgent-watcher.ts]
This listener will:
- Monitor emails from [email protected]
- Use AI to detect urgent emails (not just keywords)
- Send high-priority notifications for truly urgent emails
- Star emails that require immediate action
Use AI (context.callAgent()) when:
Use simple filtering when:
Default to AI unless the filter is purely identity-based.
For time-based actions (daily summaries, weekly reports):
export const config: ListenerConfig = {
id: "daily_summary",
name: "Daily Email Summary",
enabled: true,
event: "scheduled_time"
// Note: Cron schedule configured separately in scheduler
};
export async function handler(
data: { timestamp: Date },
context: ListenerContext
): Promise<void> {
// Your scheduled logic here
await context.notify("Good morning! Your daily summary...");
}
Note: Scheduled listeners require cron scheduler configuration outside the listener file.
Full specification: See project root LISTENERS_SPEC.md for complete details on:
Analyzes your recent Claude Code chat history to identify coding patterns, development gaps, and areas for improvement, curates relevant learning resources from HackerNews, and automatically sends a personalized growth report to your Slack DMs.
Automate Brevo (Sendinblue) tasks via Rube MCP (Composio): manage email campaigns, create/edit templates, track senders, and monitor campaign performance. Always search tools first for current schemas.
Automate Klaviyo tasks via Rube MCP (Composio): manage email/SMS campaigns, inspect campaign messages, track tags, and monitor send jobs. Always search tools first for current schemas.
You are an expert in email marketing and automation. Your goal is to create email sequences that nurture relationships, drive action, and move people toward conversion.
Email has the highest ROI of any marketing channel. $36 for every $1 spent. Yet most startups treat it as an afterthought - bulk blasts, no personalization, landing in spam folders.
Write personalized trade show booth invitation emails, pre-show outreach sequences, and exhibition meeting requests. Use this skill when the user needs to invite prospects, customers, partners, or VIPs to visit their booth at a trade show or exhibition, write pre-show emails, create multi-touch invitation sequences, draft meeting requests for an upcoming event, or write any outreach related to an exhibition or expo they're attending. Triggers on requests like 'write an email inviting people to our booth', 'I need a pre-show outreach sequence for MEDICA', 'draft an invitation for our CES booth', 'help me get meetings before the trade show', 'booth traffic email', 'pre-show marketing email', 'trade show outreach template', 'exhibition invitation letter', or casual phrasing like 'we're exhibiting next month, need to get people to come by'. If the user mentions an upcoming show and wants to write emails to drive booth traffic or schedule meetings, this is the right skill.
Email has the highest ROI of any marketing channel. $36 for every $1 spent. Yet most startups treat it as an afterthought - bulk blasts, no personalization, landing in spam folders. This skill covers transactional email that works, marketing automation that converts, deliverability that reaches inboxes, and the infrastructure decisions that scale. Use when: keywords, file_patterns, code_patterns.
Create post-trade-show follow-up email sequences, lead nurture campaigns, and meeting recap emails. Use this skill when the user needs to follow up with leads after a trade show, exhibition, expo, or conference — including tiered email sequences (hot/warm/cold), thank-you emails, meeting recap emails, badge-scan follow-ups, or any post-event outreach. Triggers on phrases like 'follow up after the show', 'post-show emails', 'write a thank you to people we met at [event]', 'we collected 200 leads at [show], help me write follow-up', 'the show just ended, now what', 'convert trade show leads into pipeline', 'trade show lead follow-up template', 'post-event email sequence', 'convert trade show leads', 'follow up with expo contacts', or 'I have a spreadsheet of contacts from the expo'. Also use this skill if the user mentions having just returned from a trade show and wants to do something with the contacts they collected.
Take anthropics/listener-creator from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.