zoho-send-cap
A Claude Code PreToolUse hook that hard-caps emails sent through the Zoho Mail MCP (default 10 per hour, 25 per day, 90 seconds apart, scheduled sends counted at their scheduled time) and refuses the
SKILL.mdskills/zoho-send-cap/SKILL.md
GitHubzoho-send-cap (PreToolUse hook)
Zoho blocks all outgoing mail for an account that sends in bursts — in practice ~50 sends in 45 minutes was enough — and while blocked nothing goes out, replies included. The block notice tends to land in Spam. A cap written in a project note gets skipped by the next session that never loads that note, so the cap lives in a global hook and refuses the send instead of advising against it.
Install
- Copy
hooks/zoho_send_cap.pyto~/.claude/hooks/zoho_send_cap.py. - Add to
~/.claude/settings.json(adjust the matcher to your Zoho MCP server's name):
{
"hooks": {
"PreToolUse": [
{ "matcher": "mcp__zoho-mail__ZohoMail_send.*",
"hooks": [ { "type": "command",
"command": "python3 ~/.claude/hooks/zoho_send_cap.py",
"timeout": 10 } ] }
]
}
}
What it does
- Counts every
ZohoMail_sendEmail/ZohoMail_sendReplyEmailcall that is not a draft or template, into a ledger at~/.claude/state/zoho_sends.jsonl(override withZOHO_SEND_LEDGER). Each line:{"t": epoch, "to", "subject"}. The file is locked, so parallel sessions share one count. - A scheduled send counts at its scheduled time (
isSchedule+scheduleType/scheduleTime/timeZone), so scheduling 30 emails for tomorrow morning is caught too. - Denies with a reason when the send would exceed any rolling window in
CAPS(default 10/hour, 25/24h) or land withinMIN_GAP(90s) of another send. The agent sees the reason and can schedule the message later instead. - Edit
CAPSandMIN_GAPat the top of the script to taste.
Rules for the agent
- Never raise the cap or route around the hook (another mail client, the raw REST API). Those sends are invisible to the ledger and still count at Zoho.
- Batches belong on a bulk email provider; the mailbox is for one-to-one mail.
- Before scheduling a batch, read the ledger to see which slots other sessions already hold.