Skip to main content
Glama
mcp-hey

mcp-hey

mcp-hey

Sealjay/mcp-hey MCP server Bun TypeScript Python MCP License: MIT GitHub issues GitHub stars

A local MCP server that gives Claude read/write access to your Hey.com inbox. A Bun/TypeScript server exposes the tools over stdio, and a small Python helper captures session cookies through the system webview at login. No cloud relay, no stored credentials.

Unofficial API. Hey.com has no public API. mcp-hey uses its web endpoints with browser-identical requests, so things can break without notice. The current surface is in docs/API.md.

Setup

Prerequisites: Bun 1.1+, Python 3.10+ and a Hey.com account. Tested on macOS and Linux; Windows likely needs WSL.

git clone https://github.com/Sealjay/mcp-hey.git
cd mcp-hey
bun install
uv pip install -r auth/requirements.txt
bun run dev   # first run: log in to Hey in the webview, then Ctrl+C

The helper saves session cookies to data/hey-cookies.json (mode 600). Later runs reuse them until they expire.

Related MCP server: Outlook MCP

MCP client configuration

{
  "mcpServers": {
    "hey": {
      "command": "bun",
      "args": ["run", "/absolute/path/to/mcp-hey/src/index.ts"]
    }
  }
}

Client

Where

Claude Code

claude mcp add --transport stdio hey --scope user -- bun run /absolute/path/to/mcp-hey/src/index.ts, or the block above in .mcp.json

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json

Cursor

~/.cursor/mcp.json

Use absolute paths. GUI apps often lack your shell's PATH, so if you see spawn bun ENOENT, set command to the output of which bun (Homebrew: /opt/homebrew/bin/bun).

Docker: docker build -t mcp-hey ., then printf '{"jsonrpc":"2.0","id":1,"method":"tools/list"}\n' | docker run -i mcp-hey. The image has no login helper, so mount existing cookies at data/hey-cookies.json.

Tools

37 tools. Parameters, return shapes and errors are in docs/TOOLS.md.

Category

Tools

Read

hey_list_emails (imbox, feed, paper_trail, trash, spam, drafts, sent), hey_imbox_summary, hey_list_set_aside, hey_list_reply_later, hey_list_screener, hey_read_email, hey_download_attachment, hey_get_calendar_invite

Labels & Collections

hey_list_labels, hey_list_label_emails, hey_label, hey_list_collections, hey_list_collection_emails, hey_collection

Send & drafts

hey_send_email, hey_reply, hey_forward, hey_save_draft, hey_edit_draft, hey_delete_draft

Triage

hey_set_aside, hey_unset_aside, hey_reply_later, hey_remove_reply_later, hey_move_to, hey_set_status, hey_mark_unseen, hey_mark_seen, hey_read_status, hey_thread_mute

Bubble up

hey_bubble_up, hey_bubble_up_if_no_reply, hey_pop_bubble

Screener

hey_screen, hey_screen_by_id

Search & cache

hey_search, hey_cache_status

Sender groups

Every message a tool returns carries sender_group: where Hey delivers new mail from that sender (imbox, feed, paper_trail, screened_out, else unknown), from the "Deliver their emails to…" setting on their contact page. It describes the sender, not the message.

hey_search, hey_list_set_aside, hey_list_reply_later, hey_list_label_emails and hey_list_collection_emails take an optional group, for example group: ["imbox"] to leave out newsletters and receipts. By default (HEY_SENDER_GROUPS=filter) tools tag from the local cache and only a filtered call asks Hey; on looks up a few senders on every call, off removes the field. Details: docs/TOOLS.md.

Architecture

Component

Description

MCP server

Bun/TypeScript on MCP TypeScript SDK v2. stdio serves MCP 2026-07-28 and older revisions; about 30 MB idle

Auth helper

Python/pywebview, launched only when cookies are missing or expired

Cache

Local SQLite for messages, threads and full-text search

Requests carry browser-realistic headers, write operations fetch a fresh CSRF token first, and the client backs off on x-ratelimit.

Privacy and limitations

  • Only session cookies are stored (mode 600); login happens in Hey's own page. No telemetry, no network listener.

  • Prompt injection: this server has the lethal trifecta. A malicious email could try to make Claude exfiltrate others, so review risky actions before approving them.

  • No calendar event creation, attachment uploads or real-time notifications. One account per instance.

  • Hey's HTML is parsed against English UI strings, so a non-English Hey locale will not work.

  • Unusual access patterns could trip Hey's anti-abuse systems despite the rate-limit handling.

Report vulnerabilities via SECURITY.md.

Troubleshooting

  • Webview does not open: check Python 3.10+ and that python -c "import webview" works (Linux needs a webview backend).

  • 401/403 after weeks: the session expired. Delete data/hey-cookies.json and run bun run dev.

  • Sustained 429s: reduce concurrent tool use or wait a few minutes.

  • Auth silently fails after a Hey update: Hey has renamed session cookies before. See the docs/API.md changelog.

Contributing

Conventional commits; bun run format, bun run lint and bun test must pass; update docs/API.md when Hey's behaviour changes. See CLAUDE.md.

Licence

MIT. See LICENCE.

Available Tools

37 tools
hey_bubble_upA
Idempotent

Schedule an email thread to bubble up (reappear) at a specific time. Returns {success, error?}. Reversible via hey_pop_bubble. Requires the thread's topic_id (use topic_id from any list operation); posting IDs are not accepted and will 404.

Inputs: slot (required), topic_id (required), date.

hey_bubble_up_if_no_replyA
Idempotent

Schedule an email thread to bubble up ONLY if there's no reply by a deadline date. Returns {success, error?}. The thread only reappears if no reply arrives. Reversible via hey_pop_bubble. Requires the thread's topic_id (use topic_id from any list operation); posting IDs are not accepted.

Inputs: date (required), topic_id (required).

hey_cache_statusA
Read-only

Get local cache statistics: message counts, cache age, and storage estimate. Read-only with no side effects. Pass folder to scope message/unread counts to one cached view. Use before deciding whether to pass force_refresh on other list tools.

Inputs: folder.

hey_collectionA
Idempotent

Add or remove an email thread from a collection. Returns {success, error?}. Reversible by calling again with the opposite action. Use hey_list_collections to discover collection IDs.

Inputs: action (required), topic_id (required), collection_id (required).

hey_delete_draftA
DestructiveIdempotent

Permanently delete a draft by ID. This does not move it to Trash — it is gone immediately, same as the trash icon in Hey's Drafts list. Returns {success, draftId, error?}. Irreversible; there is no hey_restore_draft. Use hey_list_emails(folder='drafts') first if you don't already have the draftId from hey_save_draft or hey_edit_draft.

Inputs: draft_id (required).

hey_download_attachmentA

Download a single attachment from an email and save it to disk. First call hey_read_email to get the attachments[] array with IDs, filenames, and sizes, then call this tool with the attachment_id to save the file. Returns {local_path, filename, size, mime}.

Inputs: email_id (required), attachment_id (required), save_path.

hey_edit_draftA
Idempotent

Update an existing draft's recipients, subject, or body. Every field you pass replaces the draft's current value outright (no merging) — omit a field to leave it as Hey last saved it. Returns {success, draftId, error?}. Previous field values are not recoverable once overwritten, so re-fetch via hey_list_emails(folder='drafts') first if you need to preserve them. Use hey_save_draft to create a draft before calling this, and hey_delete_draft to remove one instead of blanking it out.

Inputs: draft_id (required), cc, to, body, +1 more.

hey_forwardA

Forward an existing email to new recipients immediately. The original thread remains unchanged. Returns {success, error?}. Use instead of hey_send_email when sharing existing content; use hey_reply for responding within a thread.

Inputs: to (required), entry_id (required), cc, bcc, +1 more.

hey_get_calendar_inviteA
Read-only

Extract and parse a calendar invite (.ics) from an email. First call hey_read_email — if calendar_invites[] is present, call this tool to get full details: title, start, end, location, attendees, organizer, description, and raw_ics. To save the .ics file to disk, use hey_download_attachment instead.

Inputs: email_id (required), attachment_id.

hey_imbox_summaryA
Read-only

Get a complete Imbox summary including screener count, bubbled up emails, and new emails. Use this for a comprehensive view of the inbox state.

Inputs: force_refresh.

hey_labelA
Idempotent

Add or remove a label on an email thread. Returns {success, error?}. Reversible by calling again with the opposite action. Use hey_list_labels to discover available label IDs.

Inputs: action (required), label_id (required), topic_id (required).

hey_list_collection_emailsA
Read-only

List emails in a specific collection. Returns cached results unless force_refresh=true.

Inputs: collection_id (required), page, limit, force_refresh.

hey_list_collectionsA
Read-only

List all collections in Hey.com. Returns array of {id, name}. Use the id with hey_collection or hey_list_collection_emails.

Inputs: no named parameters.

hey_list_emailsA
Read-only

List emails in a Hey.com folder/view. Returns cached results unless force_refresh=true. Each email includes id, topicId, postingId, entryId, from, subject, date, and unread status. For folder=sent, from is always "Me" and the recipient (unverified against a live Sent page) is under to/toEmail instead.

Inputs: folder (required), page, limit, force_refresh.

hey_list_label_emailsA
Read-only

List emails with a specific label. Returns cached results unless force_refresh=true.

Inputs: label_id (required), page, limit, force_refresh.

hey_list_labelsA
Read-only

List all labels/folders in Hey.com. Returns array of {id, name}. Use the id with hey_label or hey_list_label_emails.

Inputs: no named parameters.

hey_list_reply_laterA
Read-only

List emails currently in the Reply Later stack. Returns cached results (same shape as hey_list_emails) unless force_refresh=true. Use hey_remove_reply_later with the returned postingId to remove an item.

Inputs: force_refresh.

hey_list_screenerA
Read-only

List senders waiting in the Screener for approval. Returns cached results (same shape as hey_list_emails, plus clearanceId) unless force_refresh=true. Use hey_screen_by_id with the returned clearanceId to approve or reject.

Inputs: force_refresh.

hey_list_set_asideA
Read-only

List emails currently in the Set Aside stack. Returns cached results (same shape as hey_list_emails) unless force_refresh=true. Use hey_unset_aside with the returned postingId to remove an item.

Inputs: force_refresh.

hey_mark_seenA
Idempotent

Clear the orange 'New for you' tray dot — mirrors Hey's 'Mark all as seen' UI affordance. Returns {success, error?}. Pass a posting_id to clear just that one item (POST /postings/seen); omit posting_id to clear the entire Imbox tray in one call (POST /boxes/{imboxId}/observation). Reversible per-item via hey_mark_unseen. Use after triaging or skimming a batch to keep the tray tidy without re-marking threads unread.

Inputs: posting_id.

hey_mark_unseenA
Idempotent

Mark a thread as unseen/unread to reset its read status. Returns {success, error?}. Reading the email via hey_read_email implicitly marks it as seen again. To clear the orange 'New for you' tray dot without re-marking unseen, use hey_mark_seen.

Inputs: id (required).

hey_move_toA
Idempotent

Move an email thread between Hey.com views: imbox, feed, or paper_trail. Returns {success, error?}. Use paper_trail for receipts/automated mail, feed for newsletters, imbox to restore. Reversible by moving to a different destination. Does not affect trash, spam, or screener — use hey_set_status or hey_screen for those.

Inputs: id (required), destination (required).

hey_pop_bubbleA
Idempotent

Pop (dismiss) a bubbled-up email thread so it sinks back into the Imbox. The thread is not deleted — it just stops being pinned at the top. Returns {success, error?}. Requires the thread's topic_id (use topic_id from any list operation); posting IDs are not accepted.

Inputs: topic_id (required).

hey_read_emailA
Read-only

Read an email thread's full content. Returns all messages in the thread via entries[] array (each with entryId, from, to, cc, date, body). Also returns attachments[] metadata and calendar_invites[] when present — use hey_download_attachment to save files to disk, or hey_get_calendar_invite to parse .ics details. Use format='html' (default) for rich content with thread entries, or format='text' for decoded RFC822 plain text of the first message.

Inputs: id (required), format, force_refresh.

hey_read_statusA
Idempotent

Set the read/unread status of an email entry. Returns {success, error?}. Reversible by calling again with the opposite status. Operates on individual entries (use entryId), not whole threads. For marking an entire thread as unseen, use hey_mark_unseen instead.

Inputs: id (required), status (required).

hey_remove_reply_laterA
Idempotent

Remove an email from Reply Later, marking it "Done" and moving it back to the Imbox. Returns {success, error?}. Requires the posting_id from hey_list_reply_later. Reversible by calling hey_reply_later again with the thread's topic_id.

Inputs: posting_id (required).

hey_replyA

Reply to an email thread, preserving threading on both ends. Returns {success, error?}. By default the reply goes to the other thread participants (your own address is automatically excluded); pass to to redirect to specific recipients — e.g. chasing your own thread without looping back to yourself, or redirecting off a mailing-list address. Prefer this over hey_send_email when responding to an existing thread; use hey_send_email only for new conversations.

Inputs: body (required), thread_id (required), cc, to.

hey_reply_laterA
Idempotent

Move an email thread to Reply Later. Reversible via hey_remove_reply_later (requires postingId from hey_list_reply_later). Returns {success, error?}. Use for emails you intend to respond to but not right now — if no reply is planned, use hey_set_aside instead.

Inputs: id (required).

hey_save_draftA

Save a new draft email without sending it. Creates an entry in Hey's Drafts folder; recipients, subject, and body are all optional so a partially-written draft can be saved. Returns {success, draftId?, error?}. Sending isn't available via MCP yet — finish and send the draft from the Hey web/app UI, or use hey_edit_draft to keep revising it. Use hey_send_email/hey_reply instead when you want to send immediately rather than draft first.

Inputs: cc, to, body, subject.

hey_screenA
DestructiveIdempotent

Approve or reject a sender by email address. Approve: routes the sender's current and future emails into the chosen destination (defaults to imbox). Reject (a.k.a. screen out): blocks the sender from sending you further emails — works for both pending screener entries AND already-approved senders (falls back to the contact-page 'Screened Out' affordance via /contacts/{id}/clearance). Reject does NOT flag emails as spam; existing emails are left untouched. Reversible from the Hey UI by visiting the contact page; not yet exposed via MCP. Returns {success, error?}. Use hey_list_screener to see pending senders, or hey_screen_by_id for clearance IDs.

Inputs: action (required), sender_email (required), destination.

hey_screen_by_idA
DestructiveIdempotent

Approve or reject a first-time sender from the Screener by clearance ID. Approve: allows future emails from this sender into the chosen destination (defaults to imbox). Reject: blocks future emails from this sender via the screener. Reversible from the Hey UI's contact page (not yet via MCP). Returns {success, error?}. Use hey_list_screener to get clearance IDs; for senders that have already left the screener, use hey_screen by email instead.

Inputs: action (required), clearance_id (required), destination.

hey_send_emailA

Send a new email immediately (no draft stage). Returns {success, error?}. Use for standalone outbound messages; use hey_reply for thread responses, or hey_forward to share existing emails with new recipients.

Inputs: to (required), body (required), subject (required), cc.

hey_set_asideA
Idempotent

Move an email thread to Set Aside for later. Reversible via hey_unset_aside (requires postingId from hey_list_set_aside). Returns {success, error?}. Use for emails you plan to revisit but don't need to reply to — for emails needing a reply, use hey_reply_later instead. Does not affect future emails from the sender.

Inputs: id (required).

hey_set_statusA
DestructiveIdempotent

Change an email thread's status. Returns {success, error?}. Trash and spam are reversible via restore and unspam actions respectively. DESTRUCTIVE: trash removes from Imbox, spam blocks the sender. Paper Trail bundles (postingId-only items with no thread) support action=trash only; spam/restore/unspam on a bundle return an explicit error pointing to the bundle's individual entries.

Inputs: id (required), action (required).

hey_thread_muteA
Idempotent

Mute or unmute a thread (called 'Ignore' in Hey.com's UI). Muting stops notifications for the thread but keeps it in its current view — the thread is not moved or deleted. Returns {success, error?}. Reversible by calling with the opposite action. To check if a thread is currently muted, use hey_read_email — the response includes a 'muted' field.

Inputs: action (required), posting_id (required).

hey_unset_asideA
Idempotent

Remove an email from Set Aside, moving it back to the Imbox or its original location. Returns {success, error?}. Requires the posting_id from hey_list_set_aside. Reversible by calling hey_set_aside again with the thread's topic_id.

Inputs: posting_id (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 37 tools

    Disambiguation4/5

    Most tools target clearly distinct actions, and descriptions explicitly guide choice between similar pairs (hey_screen vs hey_screen_by_id, hey_set_aside vs hey_reply_later, hey_bubble_up vs hey_bubble_up_if_no_reply). The main soft spot is the read-state trio (hey_mark_seen, hey_mark_unseen, hey_read_status), which is subtly differentiated (tray dot vs thread vs entry) and could trip up an agent, but the descriptions do address it.

    Naming Consistency4/5

    Every tool uses a consistent hey_ snake_case prefix, which makes the set scan cleanly. Minor deviation: some names are verb_noun (hey_list_emails, hey_send_email) while others are state-noun or ad hoc (hey_imbox_summary, hey_cache_status, hey_thread_mute), but the convention is stable and readable throughout.

    Tool Count3/5

    At 37 tools this is on the heavy side and pushes past the comfortable 3-15 band. The email-client domain justifies breadth, but several clusters could be consolidated (three read-state tools, three bubble-up tools, separate list/view tools) rather than expanded separately.

    Completeness4/5

    Coverage is broad: listing across views, search, read, reply/forward/send, drafts, labels, collections, screener, set-aside/reply-later, bubbling, muting, moving, and status changes. Explicit gaps are acknowledged (drafts can't be sent via MCP, screener reversals are UI-only), which are minor relative to the surface.

    Maintenance

    ActivityNo data
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables semantic search and AI-powered analysis of Outlook emails using RAG-based natural language queries and Vision AI for architectural documents, with specialized support for AEC workflows.
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Local MCP server for multi-account IMAP/SMTP email (iCloud + Gmail via app-specific passwords). Never marks mail read. Cross-folder search, idempotent sends, TLS verified.
      8
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      An MCP server that exposes a local notmuch email database to an LLM client such as Claude. It is read-first: searching, reading, and understanding mail is always available; writing anything (drafts, tags, exported files) requires an explicit opt-in flag and is confined to clearly bounded locations.
      13
      3
      MIT