hey_thread_mute
Mute or unmute a thread in Hey.com to stop or resume notifications. The thread stays in its current view; it is not moved or deleted.
Instructions
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.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| posting_id | Yes | The posting ID of the thread (use postingId from list operations) | |
| action | Yes | mute to stop notifications, unmute to resume them |
Implementation Reference
- src/index.ts:991-1017 (registration)The 'hey_thread_mute' tool is registered in the tools array with its name, description, annotations, and inputSchema. It accepts posting_id (string) and action (enum: mute/unmute).
{ name: "hey_thread_mute", annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, description: "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.", inputSchema: { type: "object" as const, properties: { posting_id: { type: "string", description: "The posting ID of the thread (use postingId from list operations)", }, action: { type: "string", enum: ["mute", "unmute"], description: "mute to stop notifications, unmute to resume them", }, }, required: ["posting_id", "action"], }, }, - src/index.ts:1873-1903 (handler)The switch-case handler for the 'hey_thread_mute' tool call. Validates posting_id and action, then delegates to ignoreThread() for mute action or unignoreThread() for unmute action.
case "hey_thread_mute": { const postingId = validateId(args?.posting_id) const action = args?.action as string if (!postingId) { return { content: [ { type: "text", text: "Error: posting_id is required and must be valid", }, ], isError: true, } } if (!action || !["mute", "unmute"].includes(action)) { return { content: [ { type: "text", text: "Error: action is required (mute or unmute)", }, ], isError: true, } } result = action === "mute" ? await ignoreThread(postingId) : await unignoreThread(postingId) break } - src/tools/organise.ts:696-712 (handler)The ignoreThread() function performs the mute operation by POSTing to /postings/{postingId}/muting, then invalidates the cache with action 'mute'.
export async function ignoreThread(postingId: string): Promise<OrganiseResult> { if (!postingId) { return { success: false, error: "Posting ID is required" } } try { const response = await withCsrfRetry(() => heyClient.post(`/postings/${postingId}/muting`), ) return organiseResponseToResult(response, () => invalidateForAction("mute", postingId), ) } catch (err) { return { success: false, error: toUserError(err) } } } - src/tools/organise.ts:714-732 (handler)The unignoreThread() function performs the unmute operation by DELETE-ing /postings/{postingId}/muting, then invalidates the cache with action 'unmute'.
export async function unignoreThread( postingId: string, ): Promise<OrganiseResult> { if (!postingId) { return { success: false, error: "Posting ID is required" } } try { const response = await withCsrfRetry(() => heyClient.delete(`/postings/${postingId}/muting`), ) return organiseResponseToResult(response, () => invalidateForAction("unmute", postingId), ) } catch (err) { return { success: false, error: toUserError(err) } } } - src/cache/messages.ts:542-548 (helper)The cache invalidation logic for 'mute' and 'unmute' actions. It invalidates the cached message for the given postingId (but not full folder syncs, as muting doesn't move the thread between folders).
case "mute": case "unmute": // Muting/unmuting affects thread notifications but not folder membership if (messageId) { execute("UPDATE messages SET cached_at = 0 WHERE id = ?", [messageId]) } break - src/index.ts:1002-1017 (schema)The input schema for hey_thread_mute defines posting_id (string, required) and action (enum ['mute', 'unmute'], required). Also the muted field returned in readEmail result type.
type: "object" as const, properties: { posting_id: { type: "string", description: "The posting ID of the thread (use postingId from list operations)", }, action: { type: "string", enum: ["mute", "unmute"], description: "mute to stop notifications, unmute to resume them", }, }, required: ["posting_id", "action"], }, }, - src/tools/read.ts:643-661 (helper)Detection of muted/ignored thread status when reading an email, setting the muted boolean property on the response.
// Detect muted/ignored thread status const muteNotice = root.querySelector(".island--topic-notice") const muted = muteNotice?.text?.includes("ignored") || !!root.querySelector(".action-group__action--unmute") return { id, from, fromEmail, to: firstEntry?.to, cc: firstEntry?.cc, subject, body: fallbackBody || body, date, threadId, entries: entries.length > 0 ? entries : undefined, muted: muted || undefined, }