KasarKasar Docs
MCP Server

Tools — Inbox & Messaging

MCP tools to read the unified inbox, mark email threads, and send messages across Email, LinkedIn, and WhatsApp.

The Inbox & Messaging tools give your AI assistant read access to the unified inbox (Email, LinkedIn, WhatsApp) and the ability to mark email threads and send messages on your connected channels.

Visibility is exactly what you see in the app. The inbox tools mirror the app's visibility model — there is no extra token scope to unlock content. list_threads and get_thread only ever return threads you own, and the owner-bypass means you read your own message bodies (decrypted at rest, exactly as in the app's inbox). The previous inbox:content scope no longer exists.

crm_inbox

Read email and message conversations from the unified inbox (read-only).

Parameters

ParameterTypeRequiredDescription
actionlist_threads | get_thread | get_interactionYesOperation to perform.
channelall | email | meeting | linkedin | whatsappNoFilter threads by channel (list_threads). Defaults to all.
sort_bystringNoThread field to sort by (list_threads). Defaults to last_message_date.
sort_dirasc | descNoSort direction (list_threads). Defaults to desc.
cursorstringNoPagination cursor for thread listing (list_threads).
limitnumberNoMax threads per page. Default 20, max 100.
thread_idstringNoThread UUID. Required for get_thread.
message_cursorstringNoPagination cursor for messages within a thread (get_thread).
message_limitnumberNoMax messages per page (get_thread). Default 50, max 200.
interaction_idstringNoSingle message UUID. Required for get_interaction.

Key behaviors

  • App-parity visibility — You only ever see threads you own. Email/meeting threads are owned via the connected account that synced them; LinkedIn/WhatsApp threads are owned via the thread's managed-by-workspace-user. A non-owned single message fetched via get_interaction is gated by that thread's content visibility.
  • Decrypted bodies — Message bodies are decrypted at read time for threads you own, so you get the full content just like in the app.
  • Thread pagination — Listing is paginated server-side. Use cursor and limit to page through results.
  • Message pagination — Large threads paginate internally via message_cursor and message_limit. When next_message_cursor is null, all messages have been returned.

Thread detail folds in the former crm_interaction_detail tool: use action: "get_thread" for a whole conversation, or action: "get_interaction" for one message. The standalone activity feed moved to the Data page as crm_find_activity.

Examples

List the most recent email threads:

{
  "action": "list_threads",
  "channel": "email",
  "limit": 10
}

Response:

{
  "data": [
    {
      "id": "t_abc123",
      "subject": "Q3 contract renewal",
      "channel": "email",
      "last_message_date": "2026-06-10T14:22:00Z"
    }
  ],
  "total": 23,
  "nextCursor": "eyJvZmZzZXQiOjEwLCJsaW1pdCI6MTB9"
}

Get a thread with paginated messages:

{
  "action": "get_thread",
  "thread_id": "t_abc123",
  "message_limit": 20
}

Response:

{
  "thread": {
    "id": "t_abc123",
    "subject": "Q3 contract renewal",
    "channel": "email"
  },
  "messages": [
    {
      "id": "msg_001",
      "date": "2026-06-10T14:22:00Z",
      "body": "Hi Bob, following up on the renewal..."
    }
  ],
  "total_messages": 4,
  "next_message_cursor": null
}

Get a single message by id:

{
  "action": "get_interaction",
  "interaction_id": "550e8400-e29b-41d4-a716-446655440000"
}

crm_mark_email

Mark an email thread as read/unread or favorite/unfavorite. Email channel only.

Parameters

ParameterTypeRequiredDescription
actionread | unread | favorite | unfavoriteYesread/unread toggle the unread state; favorite/unfavorite toggle the star.
thread_idstringYesCRM thread id (interaction_threads.id) of an email thread you own.

Key behaviors

  • Email only — These map to Gmail labels (UNREAD / STARRED) or Outlook flags in your own mailbox. A non-email thread_id is rejected with INVALID_CHANNEL.
  • Ownership-checked — You can only act on a thread you own (you must have synced an interaction in it). Otherwise you get PERMISSION_DENIED.
  • Provider-resolved — The handler resolves the CRM thread to its external thread id (external_thread_id) and to the connected account that synced it, then applies the change in the provider mailbox. The CRM stores no read/favorite state — it lives in your mailbox.
  • Reversible — Every action has its inverse (readunread, favoriteunfavorite).

Get the thread_id from crm_inbox or crm_find_activity.

Examples

Mark a thread as read:

{
  "action": "read",
  "thread_id": "t_abc123"
}

Response:

{
  "thread_id": "t_abc123",
  "action": "read",
  "channel": "email",
  "applied": true
}

Star (favorite) a thread:

{
  "action": "favorite",
  "thread_id": "t_abc123"
}

crm_send_message

Send a message on a connected channel — Email, LinkedIn, or WhatsApp.

Mandatory two-step gate (enforced server-side). First call action: "preview" — this resolves the sender account, recipient/thread, and validates prerequisites without sending, returning the exact message that would be sent plus a single-use confirm_token. Present that preview to the user, then call action: "send"echoing the confirm_token — only after the user explicitly confirms. The send is rejected without a matching token (CONFIRM_REQUIRED), and the token is bound to the exact message previewed (changing a recipient or the body invalidates it → CONFIRM_MISMATCH). Sending is irreversible.

Parameters

ParameterTypeRequiredDescription
actionpreview | sendYespreview validates and returns the message without sending; send actually sends (only after user confirmation).
channelemail | linkedin | whatsappYesChannel to send on.
bodystringYesMessage body. For email this is HTML.
thread_idstringNoReply within this existing CRM thread (interaction_threads.id). Required for LinkedIn; optional for email; for WhatsApp it resolves the chat.
tostring[]NoEmail: recipient addresses. Required for email.
ccstring[]NoEmail: CC addresses.
bccstring[]NoEmail: BCC addresses.
subjectstringNoEmail: subject. Required for email; for a reply, set it yourself (e.g. "Re: ...").
from_account_emailstringNoEmail: which connected account to send from. Optional if you have exactly one connected account; required if several (preview returns the list).
chat_idstringNoWhatsApp: target chat id (e.g. 33XXXXXXXXX@c.us). Not needed when thread_id is given.
confirm_tokenstringOn sendThe single-use token returned by the matching preview call. Echo it verbatim to authorize sending the exact previewed message.

Per-channel requirements

Requires to, subject, and body (HTML). from_account_email is auto-picked when you have exactly one connected email account, otherwise specify it (AMBIGUOUS_SENDER lists the available accounts). Send must be enabled on the account (SEND_NOT_ENABLED otherwise — reconnect to grant send permission). Pass thread_id to thread a reply; you must own that email thread.

Reply-only: requires an existing thread_id (a LinkedIn conversation you own, found via crm_inbox) plus body. Requires an active LinkedIn connection.

Provide either thread_id (a WhatsApp thread you own — resolves the chat) or chat_id (e.g. 33XXXXXXXXX@c.us), plus body. Requires an active WhatsApp connection.

Key behaviors

  • Own accounts only — You can only send from your own connected accounts, and can only reply within a thread you own. Ownership is re-checked on every send.
  • Per-channel validation — Preview surfaces missing/ambiguous inputs as structured errors (MISSING_REQUIRED_FIELD, AMBIGUOUS_SENDER, NO_ACCOUNT, PERMISSION_DENIED, INVALID_CHANNEL, SEND_NOT_ENABLED) before anything is sent.
  • Stateful confirmation — The confirm_token from preview is single-use, expires after ~5 minutes, and is bound to the message fingerprint. A send with no/expired token returns CONFIRM_REQUIRED/CONFIRM_INVALID; one whose recipients or body changed since preview returns CONFIRM_MISMATCH. (If the rate-limit/confirmation store is unavailable the gate fails open so an outage can't block legitimate sends.)
  • Idempotent — An identical message sent twice in quick succession is de-duplicated server-side: the second call returns mode: "duplicate_suppressed" and is not delivered again.
  • Rate limited — Real sends are capped (api:messaging-send, 30/hour per user); previewing is free.

Examples

Step 1 — preview a new email:

{
  "action": "preview",
  "channel": "email",
  "to": ["bob@acme.com"],
  "subject": "Q3 contract renewal",
  "body": "<p>Hi Bob, following up on the renewal...</p>",
  "from_account_email": "alice@example.com"
}

Preview response:

{
  "mode": "preview",
  "confirm_required": true,
  "confirm_token": "a1b2c3d4-…",
  "message": {
    "channel": "email",
    "from": "alice@example.com",
    "to": ["bob@acme.com"],
    "cc": [],
    "bcc": [],
    "subject": "Q3 contract renewal",
    "body": "<p>Hi Bob, following up on the renewal...</p>",
    "reply_to_thread": null
  },
  "note": "Present this message to the user. Call again with action=\"send\" AND echo this confirm_token ONLY after the user has explicitly confirmed. Sending is irreversible."
}

Step 2 — send after the user confirms (echo the confirm_token from step 1):

{
  "action": "send",
  "channel": "email",
  "to": ["bob@acme.com"],
  "subject": "Q3 contract renewal",
  "body": "<p>Hi Bob, following up on the renewal...</p>",
  "from_account_email": "alice@example.com",
  "confirm_token": "a1b2c3d4-…"
}

Sent response:

{
  "mode": "sent",
  "channel": "email",
  "message_id": "msg_001",
  "message": {
    "channel": "email",
    "from": "alice@example.com",
    "to": ["bob@acme.com"],
    "subject": "Q3 contract renewal",
    "body": "<p>Hi Bob, following up on the renewal...</p>"
  }
}

Reply on LinkedIn (preview):

{
  "action": "preview",
  "channel": "linkedin",
  "thread_id": "t_li_456",
  "body": "Thanks for connecting — happy to set up a call next week."
}

Send a WhatsApp message by chat id:

{
  "action": "send",
  "channel": "whatsapp",
  "chat_id": "33612345678@c.us",
  "body": "Hi! Just confirming our meeting tomorrow at 10am."
}

On this page