CommSync
One inbox for business SMS and email.
What it can do
- List Threads: List conversation threads, newest first. Org-scoped: only threads on channels the caller can access are returned. Supports cursor pagination, view selection, label filtering, and an opti
- Get Thread Summary: Get a single thread summary (the same shape returned by list_threads). Returns 404-style error when the thread is missing or hidden by channel-RBAC for this user.
- Get Thread Messages: Get messages for a thread, ordered by time. Supports polling (afterId) and backwards pagination (before). Returns null when the thread is invisible to this user.
What data it sees
Do you need an account
No: the server works without sign-in
One inbox for business SMS and email. CommSync puts every phone line (Twilio, JustCall, Quo, RingCentral, Telnyx, and more) and every mailbox (Gmail, Outlook, any IMAP account) in one shared inbox, with contacts that tie a person's numbers and addresses together.
- Search and read SMS and email threads, with message history and match snippets
- Triage: mark read, archive, snooze, and file threads to Spam, Promotions, or Automated
- Reply by SMS or email, start new conversations, and forward messages from the lines you allow
- Look up, create, update, and merge contacts and their phone and email identities
- Read the Daily Brief and manage outbound webhooks
Sign in with OAuth and choose which lines the app can use. Every tool is annotated; sends and deletes are marked destructive.
How to connect
How to connect CommSync to Claude
The server address to paste into Claude settings:
https://commsync--commsync.run.tools
- Open Claude (claude.ai or the desktop app).
- Go to Settings → Connectors.
- Click Add custom connector.
- Paste the server address copied below into Remote MCP server URL and click Add.
- In a chat, click + → Connectors and switch the new connector on.
Custom connectors are available on Free, Pro, Max, Team and Enterprise plans (Free is limited to one). On Team and Enterprise an organization Owner adds the connector first under Organization settings → Connectors.
Authorization
After you click Add, a sign-in window for the service opens. Sign in with your own account and approve access. Claude never sees your password.
How to connect CommSync to ChatGPT
The server address to paste into ChatGPT settings:
https://commsync--commsync.run.tools
- Open ChatGPT in a browser (chatgpt.com). A Plus, Pro, Business, Enterprise or Edu plan is required.
- Turn on developer mode once: Settings → Apps → Advanced settings → Developer mode.
- Open Settings → Connectors and click Create.
- Fill in the form: Name (anything), Description (one line about what the service does), MCP server URL (copy it below).
- Under Authentication choose OAuth if the service requires sign-in, otherwise None. Click Create.
- In a new chat open + → Apps/Connectors and enable the connector.
OpenAI has renamed this section before (Connectors → Apps/Plugins). If the label differs, search settings for "developer mode". On Business/Enterprise workspaces an admin must allow custom connectors first.
Authorization
On first use ChatGPT opens the service's sign-in window. Sign in and approve access.
How to connect CommSync to Cursor
The server address to paste into Cursor settings:
https://commsync--commsync.run.tools
Fastest: click Open in Cursor below and confirm the prompt.
Manually:
- In Cursor open Settings → Cursor Settings → MCP and click Add new global MCP server.
- Paste the JSON copied below into
~/.cursor/mcp.json(per project:.cursor/mcp.jsonin the repo root). - Save the file. The server appears in the MCP list; authorize it there if asked.
Authorization
If the service needs sign-in, an authorize button appears next to the server in the MCP list.
Server tool list (60)
Raw names from tools/list. Only developers need these.
| list_threads | List conversation threads, newest first. Org-scoped: only threads on channels the caller can access are returned. Supports cursor pagination, view selection, label filtering, and an optional receiving-line filter (toPhone/toEmail) restricting results to threads touching the given org-owned phone numbers or mailboxes. |
| get_thread_summary | Get a single thread summary (the same shape returned by list_threads). Returns 404-style error when the thread is missing or hidden by channel-RBAC for this user. |
| get_thread_messages | Get messages for a thread, ordered by time. Supports polling (afterId) and backwards pagination (before). Returns null when the thread is invisible to this user. |
| mark_thread_read | Mark a thread as read for the caller, clearing this user's unread count. Other users on shared channels are unaffected. |
| mark_thread_unread | Mark a thread as unread for the caller. Bumps this user's TUS.unreadCount to 1; the cron worker reconciles the actual count on the next pass. |
| archive_thread | Archive a thread for the caller. Per-user mutation — does not affect other users on shared channels. |
| unarchive_thread | Unarchive a thread for the caller. |
| mark_thread_spam | Move a thread into Spam for the caller (mutually exclusive with Archived/Promotions/Automated). |
| mark_thread_promotions | Move a thread into Promotions for the caller (mutually exclusive with Archived/Spam/Automated). |
| mark_thread_automated | Move a thread into Automated Messages for the caller — important but non-human mail like receipts, codes, and alerts (mutually exclusive with Archived/Spam/Promotions). |
| move_thread_to_inbox | Clear the Archived/Spam/Promotions/Automated sticky flags so the thread shows up in Inbox again. |
| snooze_thread | Snooze a thread for the caller until a future timestamp. The worker re-surfaces it (clears snoozedUntil) when the time arrives. |
| unsnooze_thread | Clear a snooze on a thread for the caller. |
| delete_thread | Soft-delete a thread for the caller (move to Trash). The canonical Thread persists for other users on shared channels. The trash-sweep worker hard-deletes this user's view after the retention window. |
| restore_thread | Restore a soft-deleted thread (clear TUS.deletedAt) for the caller. |
| delete_message | Hide a single message from the caller (sets MessageUserState.accessRevokedAt). The canonical Message row persists for other users on shared channels. |
| hard_delete_thread | OWNER/ADMIN only. Hard-delete the canonical Thread row and cascade its messages. Affects every user on shared channels. A per-user soft delete (move to Trash) is a separate tool. |
| send_sms | Send an SMS reply within an existing thread. The thread must be visible to the caller and the resolved fromPhoneNumber must be a channel the caller can access. CommSync accepts the send and owns delivery from there, including provider rate limits: it paces, backs off, and retries, so this tool does not return a 429. The response carries `state` and `estimatedSendAt`. A `state` of "waiting_on_line" means the message is queued and goes out on its own. Each call sends a new message. |
| send_email | Send an email reply within an existing thread. Thread visibility + channel access are enforced before send. Give bodyText, bodyHtml, or both. At least one is required. Plain text is fine: CommSync builds the HTML part. CommSync accepts the send and owns delivery from there, including provider rate limits: it paces, backs off, and retries, so this tool does not return a 429. The response carries `state` and `estimatedSendAt`. A `state` of "waiting_on_line" means the message is queued and goes out on its own. Each call sends a new message. |
| compose_sms | Start a new SMS conversation with a phone number. Creates the identity and thread if needed. CommSync accepts the send and owns delivery from there, including provider rate limits: it paces, backs off, and retries, so this tool does not return a 429. The response carries `state` and `estimatedSendAt`. A `state` of "waiting_on_line" means the message is queued and goes out on its own. Each call sends a new message. |
| compose_email | Start a new email conversation with an email address. Creates the identity and a fresh RFC 5322 thread. Give bodyText, bodyHtml, or both. At least one is required. Plain text is fine: CommSync builds the HTML part. CommSync accepts the send and owns delivery from there, including provider rate limits: it paces, backs off, and retries, so this tool does not return a 429. The response carries `state` and `estimatedSendAt`. A `state` of "waiting_on_line" means the message is queued and goes out on its own. Each call sends a new message. |
| forward_message | Forward a message to a new recipient. Source thread must be visible to the caller. CommSync accepts the send and owns delivery from there, including provider rate limits: it paces, backs off, and retries, so this tool does not return a 429. The response carries `state` and `estimatedSendAt`. A `state` of "waiting_on_line" means the message is queued and goes out on its own. Each call sends a new message. |
| get_send_capacity | Report how fast CommSync can currently send on each line the caller can access, and whether any line is waiting out a provider rate limit. `sustainedPerMinute` is the rate a line holds without queueing. A faster rate is not an error: CommSync queues and paces everything it accepts, so the extra messages wait longer. `waitingUntil` is set when a provider asked CommSync to pause; messages queued on that line go out on their own once it clears. |
| resend_failed_message | Re-queue a failed outbound message for retry. Thread must be visible. |
| list_contacts | List all contacts (people) with their identities (max 500). |
| get_contact | Get a single contact by ID with identities and labels. |
| create_contact | Create a new contact (person) with a display name and optional notes. |
| update_contact | Update a contact display name and/or notes. |
| delete_contact | Delete a contact. All identities become orphans with their own threads. |
| merge_identities | Merge two identities under one contact. If both are orphans, a new contact is created. If one belongs to a contact, the other joins it. |
| merge_contacts | Merge whole contacts: fold one or more source contacts (all channels, labels, notes, team memberships, shared-contact promotion, and conversations) into a surviving contact, then delete the emptied sources. The survivor keeps its name. Irreversible as a whole (individual channels can still be split off later). |
| split_identity | Detach an identity from its contact, creating a new orphan thread for it. |
| attach_identity_to_contact | Attach an orphan identity to an existing contact, merging their threads. |
| promote_identity_to_contact | Promote an orphan identity to a new contact with a display name. |
| get_identity | Get a single identity (phone number or email address) with its associated contact. |
| update_identity_notes | Update the notes on an identity (channel-level note, not the contact note). |
| list_orphaned_identities | List identities not attached to any contact (max 500). These are candidates for creating a new contact or attaching to an existing one. |
| list_all_identities | List all identities (phone numbers and email addresses) with their associated contact (max 500). |
| list_labels | List all labels with usage counts (how many contacts and identities carry each label). |
| create_label | Create a new label with a name and hex color (e.g. "#F97316"). |
| update_label | Update a label (name, color, AI management settings). |
| delete_label | Delete a label. This removes the label from all contacts and identities. |
| assign_label | Assign a label to a contact (personId) or an identity (identityId). Provide exactly one target. |
| unassign_label | Remove a label from a contact (personId) or an identity (identityId). Provide exactly one target. |
| search | Search across contacts, identities (phone/email), message content, and attachment filenames. Substring and typo-tolerant. Supports Apple Mail-style query modifiers on the messages category: from:<val>, to:<val>, subject:<val>, has:attachment, in:inbox|sent|archived|trash, is:unread|starred|read, before:YYYY-MM-DD, after:YYYY-MM-DD. Modifiers combine freely ("from:kevin has:attachment after:2026-01-01"). Message snippets come back with <mark>…</mark> around matched terms. |
| search_threads | Full thread-centric search — every conversation matching the query, ranked and paginated. Matches message content, thread titles (incl. AI titles), group-thread participants, contact names, and attachment filenames. Returns thread summaries with match context (matched message id + <mark> snippet). Default scope searches everything except Spam & Trash; narrow with the scope filter or in: modifiers. |
| get_ai_settings | Get the current AI feature configuration (label classifier, inbox classifier, daily digest). |
| update_ai_settings | Update AI feature configuration. |
| get_todays_digest | Get the AI daily digest for today (or a specific date). Returns missed replies, priorities, and suggested actions. |
| list_digests | List recent AI daily digests. |
| dismiss_digest | Mark a daily digest as dismissed. |
| trigger_digest_run | Manually trigger a daily digest generation for today or a specific date. |
| list_ai_runs | List recent AI run audit entries (label classifications, inbox classifications, digests). |
| test_ai_connectivity | Report whether the AI service (OpenRouter) is configured on this server. This is a configuration check only: it makes no network call and does not prove that the service is reachable. |
| trigger_inbox_backfill | Enqueue an inbox-classifier backfill across the caller’s historical mail. Classifies each not-yet-classified sending identity as PROMOTIONS / SPAM / NORMAL and auto-files its threads into the per-user Spam / Promotions buckets — one classification per sender, since the verdict applies to all of that sender’s threads. `unclassifiedSenders` is the upper bound the chain will scan. Completion is stamped on `inboxBackfillCompletedAt` in the AI settings when the last batch finishes. Senders the user has replied to, already filed by hand, or already classified are skipped. |
| list_phone_numbers | List phone numbers the caller can access in their org. |
| add_phone_number | Add a phone number to the caller's org. Routes through linkChannel for dedup + cross-org uniqueness + tier-cap. Returns existing channel id when the number is already linked in the same org; surfaces CONFLICT_OTHER_ORG when locked to another workspace. |
| delete_phone_number | Delete a phone number. OWNER/ADMIN only. Historical messages are preserved. |
| list_email_accounts | List email accounts (IMAP/SMTP) the caller can access in their org. Credentials are never returned. |
| add_email_account | Add an IMAP/SMTP email account to the caller's org. Tests connectivity before persisting; password is encrypted at rest. Returns existing channel id when the address is already linked in the same org. Returns a CONFLICT_OTHER_ORG result when the address is locked to a different workspace. |