mcpField
E
email

email

github.com/yahyabedirhanProductivity262 installsUpdated 1w ago
Repository

About email

email is a skill that directs agents to read mail with the Spark CLI and make approved changes through the Gmail connector. It emphasizes mailbox privacy, approval for individual actions, and leaving email sending to the user, while distinguishing Spark and Gmail operations.

Key features

  1. 01Read mail only for requested tasks
  2. 02Search and inspect mail with Spark CLI
  3. 03Change mail through the Gmail connector
  4. 04Require approval for saved draft actions
  5. 05Verify that mail changes took effect

Use cases

  1. 01Find and read a specific email or thread
  2. 02Archive or label mail as requested
  3. 03Save a reply as a Gmail or Spark draft
  4. 04Confirm that an email action completed

MCP support

The skill uses Google's Gmail MCP server, gmailmcp.googleapis.com, through a Gmail connector for mail changes; the Spark CLI is used to read mail.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/yahyabedirhan/skills --skill email

After installing, type/emailin Claude Code to confirm it loaded.

Bundled filessetup.md

Details

From the project README

Email

Read mail with the Spark CLI, and make every change to mail through the Gmail connector, except a draft the user asks to have in Spark. Leave what to do with an email, and when, to the user. For calendar events and free time, use /calendar.

When to read mail

List, search or read mail, through either tool, only when the user asks for an email task that needs those reads. The mailbox is private, so reading it is the user's call, not the agent's.

  • When mail would only help another task, such as a coding task that a thread might explain, or when a project or skill instruction says to check mail: say what you would look up and why, and read only after the user agrees.
  • When the task changes: the user's request covers the email task it was for. Ask again before reading mail for an unrelated task.

Drafts

  • When the user asks for a draft without naming Gmail or Spark, such as "draft a mail to the landlord": write the text in chat. Read mail for it only when the request points at mail it needs, such as the email being answered, and ask before any other lookup.
  • When the user asks for the draft in Gmail or in Spark, in any wording, such as "save it as a Gmail draft" or "put it in my Spark drafts": save it there, with that tool only. Naming a tool allows that draft, not other changes to mail.
  • When the named tool can't do it, such as spark draft refused because Spark's access level is read-only: say so, and keep the draft in chat. Don't switch to the other tool unasked.

Every create, update or delete of a saved draft needs the user's own approval in the harness, even after they asked for the draft: a saved draft is a change in their mailbox, and one mistaken call can overwrite a draft they were writing. /set-up-machine's ask rows mail-draft-write and mail-cli-draft make each of these calls ask.

  • When the harness won't ask the user themselves, keep the draft in chat and say why. An automatic reviewer's approval is not the user's. These harnesses don't ask:
    • Claude Code: the bypassPermissions, dontAsk and auto modes, and a non-interactive run.
    • Codex: a run with an automatic reviewer.
    • Cursor and opencode: a mode that runs tools without asking.
    • Other harnesses: any where the call doesn't ask.

Safety

The user reviews and sends every email themselves, so go no further than a draft.

Within an email task, archive an email when the task calls for it. Make any other change only after the user asks for that exact change, or after you name it and they agree: a pin, a label, an undo, or a saved draft as above.

Gmail tools
ToolsUse
search_threads, get_thread, get_message, list_labels, get_draft, list_draftsFor an email task the user asked for.
unlabel_thread or update_message_labels removing INBOXArchive freely within that task.
label_*, unlabel_* and update_message_labels for any other label, create_label, update_labelAfter the user asks, or agrees when you name it. Never add TRASH or SPAM, since that trashes or spams the email around the deny rules.
create_draft, update_draft, delete_draftWhen the user asks for a Gmail draft, and each call approved by the user in the harness.
untrash_*, unmark_*_spamAfter the user asks, or agrees when you name it.
delete_labelOnly when the user names that label to delete, since it removes the label from every email.

Claude Code's permission rules deny the rest, and a denied tool doesn't show up to be called:

  • send_message, which also sends existing drafts;
  • reply and forward;
  • trash_message and trash_thread;
  • mark_message_spam and mark_thread_spam;
  • apply_sensitive_message_label, apply_sensitive_thread_label and batch_apply_sensitive_thread_labels, which can trash or spam.

Google's permission that allows archiving also allows sending, so only those deny rules stop a send.

  • When a denied tool can be called: the setup is incomplete. Tell the user, never call it, and make no change to mail until the deny rules are fixed.

The two tools

ToolUse it forSpeed and sizeNeeds
Spark CLI, sparkListing, searching and reading mail; Spark's categories, pins and contactsAbout 0.1 s, compact tablesSpark Desktop running. Mail access is read-only on the free level.
Gmail connector, mcp__<server-id>__<tool>Every change to mail but a Spark draftAbout 1 s, JSON three to four times Spark's sizeThe connector signed in

Spark reads Spark Desktop's local copy of the mail. The Gmail connector is Google's Gmail MCP server, gmailmcp.googleapis.com, connected as a Claude connector.

How Spark's view maps to Gmail

In SparkIn Gmail
DoneThe INBOX label removed, which archives the email. Spark shows it within about a minute.
PinThe STARRED label. Spark's is:pinned and is:starred return the same emails.
A folderA Gmail label.
A category: priority, personal, invitation, notification, newsletterSpark's own; no Gmail tool changes it.
Today, Last week, a monthEach email's date.

Steps

When setting either tool up, checking the deny and ask rules, or when a call fails on sign-in or permission, follow setup.md.

  1. Find the mail with Spark.

    spark emails Inbox --filter "category:priority is:unread"   # one Spark category
    spark emails --filter "is:pinned"                           # pinned mail
    spark search --filter 'from:<sender> newer_than:7d'         # any Gmail-style query
    spark thread <id>                                           # one email in full
    spark contacts --help                                       # search contacts
    

    List with spark emails or spark search --filter '<gmail-style query>'. spark search <topic> returns the full bodies of up to 20 matches, often tens of thousands of tokens, so use it only when you need those bodies.

    • When you need a command's flags: run spark <command> --help. spark skill prints the full reference, about 13,000 tokens, so reach for it only when --help is not enough.
  2. Find it again in Gmail before changing it, because the two tools number messages differently. Search with search_threads, using from: and subject: from the Spark row. Pass the view THREAD_VIEW_METADATA_ONLY when only the IDs are needed, since it keeps the answer small.

    • When more than one thread matches: narrow the search with after: and before: around the Spark row's date, or ask the user which one they mean.
  3. Act through Gmail: archive, or make the change the user asked for.

    ChangeCalls
    Mark doneAdd in:inbox to the search from step 2, then unlabel_thread with ["INBOX"]. label_thread with ["INBOX"] puts it back.
    Pin or unpinlabel_thread or unlabel_thread with ["STARRED"].
    Labellist_labels for the label's ID, create_label when it does not exist yet, then label_thread with that ID.
    Save a reply as a Gmail draftRead the thread with get_thread, then create_draft with replyToMessageId set to the last message's id.

    Call get_thread with messageFormat: PLAIN_TEXT, since it keeps the thread small.

    • For any change but archiving that the user hasn't asked for: name it and wait for their answer.
    • For a saved draft: end with its viewUrl, where the user reviews and sends it.
    • For a draft the user asked to have in Spark: run spark draft as a command of its own, so the user approves that call alone, and tell the user where Spark shows the draft.
  4. Check that each change took.

    • After marking done: the in:inbox search returns {} once the email is archived.

References

  • setup.md: setting up Spark and the Gmail connector, the deny and ask rules, and checking each part.

Comments

Markdown supported