Signal MCP
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| send_messageA | Send a text message to one Signal contact (end-to-end encrypted). Use send_group_message for groups, send_attachment for files, send_note_to_self for your own Note to Self, schedule_message to send later. Address the contact by recipient (E.164, e.g. +4915112345678) or username (alice.42 or a username link) — exactly one, otherwise an error. message is the text; formatting=true turns bold, italic, |
| send_group_messageA | Send a text message to a Signal group (end-to-end encrypted to all members). Use send_message for a single contact, send_group_attachment for files. group_id comes from list_groups. message is the text. @mentions: put the name in the text and pass mentions [{start, length, author (E.164)}]; start/length are UTF-16 code units, not codepoints — an emoji before the mention shifts the offset by 2, not 1. formatting=true turns bold, italic, |
| send_note_to_selfA | Send a message to your own Note to Self chat; it syncs to all your linked Signal devices. Use for reminders, bookmarks or drafts; use send_message to message anyone else. message always supports bold, italic, |
| edit_messageA | Replace the text of a message you already sent (DM or group); recipients see the new text with an '(edited)' label. Use to fix typos or update information; use delete_message / delete_group_message to retract it instead, and send a new message to reach different people. Only the text changes — attachments, quotes and reactions stay. target_timestamp: ms timestamp of the original (from the send_* result or the message id in get_conversation). message: the new full text (no formatting conversion). Give recipient (E.164) for a DM or group_id (from list_groups) for a group; neither is an error. Signal only accepts edits of your own messages. Contacts Signal's servers and overwrites the stored body locally (the old text is not kept); repeating the same edit has no further effect. Returns {status, target_timestamp}. |
| receive_messagesA | Poll the signal-cli daemon once for newly arrived messages and save them to the local store. Normally use get_unread instead — it polls when needed and returns unread messages in one call; use receive_direct only if the daemon is stuck. timeout: seconds to wait (integer, default 5); max_messages: stop after this many (default: no limit). Incoming edits and remote deletes are applied to stored messages instead of being returned; receipts are returned but not stored. Does not mark anything read. Returns a list of messages (id, sender, sender_name, recipient, group_id, group_name, body, timestamp, attachments, quote_id, reactions, is_read). If the background service already holds the receive lock, returns {note, messages} with up to 50 unread messages from the store instead. |
| receive_directA | Troubleshooting fallback: receive messages by running signal-cli receive directly, bypassing the daemon. Use only when receive_messages / get_unread fail because the daemon is stuck; it stops the daemon (holding a lock file meanwhile), runs the receive, and the daemon restarts on the next call. timeout: seconds to wait (default 5); max_messages: stop after this many (default: no limit); ignore_attachments, ignore_stories, ignore_avatars, ignore_stickers (all default false) skip downloading those. Received messages are saved to the local store and remote deletes applied; nothing is marked read. Returns a list of messages in the same shape as receive_messages; errors if signal-cli exits non-zero. |
| list_contactsA | List the contacts in signal-cli's local contact store (no network call). Use it to browse or audit contacts; to look up one person's number by name, find_contact is the shorter call. Parameters: search (optional) keeps only contacts whose number, name, given/family name, nickname or username contains it (case-insensitive); all_recipients (default false) also includes people not in your address book, e.g. members of your groups; blocked=true returns only blocked contacts, false only unblocked, omitted all. Returns a list of objects with number (E.164), uuid, name, given_name, family_name, about, blocked, display_name, plus username, nick_name, note, has_avatar, message_expiration_time etc. when set. |
| list_groupsA | List the Signal groups this account knows, from signal-cli's local store (no network call). Call it first to get the group_id that send_group_message, send_group_attachment, update_group, leave_group, terminate_group and set_expiration_timer require, or to check your admin status. group_id (optional) returns only that group. Each group has id, name, description, member_count, members (uuid, number, is_admin, and label/label_emoji — the tag shown next to the name — when set), is_blocked, is_member and invite_link; when non-empty also pending_members (invited), requesting_members (awaiting approval), banned, permission_add_member/permission_edit_details/permission_send_message (EVERY_MEMBER or ONLY_ADMINS), message_expiration_time (seconds) and is_terminated. |
| get_conversationA | Read the message history of one conversation from the local store (no Signal server call). Use list_conversations to find conversations, search_messages to find text across all chats, get_unread for only new messages. recipient: E.164 number for a DM or a group_id (from list_groups). limit: max messages (default 50, clamped 1-500); offset: skip the newest N for paging back (default 0); since: only messages at or after this ISO datetime (e.g. 2024-01-01T00:00:00; invalid values return an error). Side effect: incoming messages returned are marked read in the local store only — no read receipt is sent (use send_read_receipt). Returns {messages (oldest first; id, sender, sender_name, body, timestamp, attachments, quote_id, reactions, is_read …), total, has_more, limit, offset}. Timestamps are milliseconds: a message's id in get_conversation is its timestamp (sent messages: sent__). |
| search_messagesA | Full-text search of message bodies across all conversations in the local store (SQLite FTS; no Signal server call, nothing marked read). Only messages stored on this device are found. Use get_conversation to read a chat in order. query: words to find (all words must occur; falls back to substring match if FTS fails; empty returns []). sender: only messages from this E.164 number. since (inclusive) / until (exclusive) ISO dates, e.g. since=2024-01-01, until=2024-01-02 covers all of Jan 1; invalid dates return an error. limit: max results (default 50, clamped 1-500); offset: skip N results for paging (default 0). Returns a list of messages, newest first (id, sender, sender_name, recipient, group_id, group_name, body, timestamp, attachments …). |
| send_attachmentA | Send one or more files (photos, videos, documents, audio) to one Signal contact in a single message. Use send_group_attachment for groups, send_message for text only, send_sticker for stickers. Address by recipient (E.164) or username (alice.42 or username link) — exactly one. path: a single file; paths: several files sent together (one of the two is required). Files must lie inside the allowed send folders (default: the signal-mcp attachments folder, ~/Downloads, ~/Desktop, ~/Documents; override with SIGNAL_MCP_SEND_ROOTS) and not be hidden, else an error is returned. caption: text shown with the files (default empty); view_once=true lets the recipient open media only once; voice_note=true marks audio as a voice note. attachment_dimensions (WIDTHxHEIGHT per file, in order) and attachment_blurhash (BlurHash per file, in order) give clients a size and a preview before download; an empty string skips a file. Reply/quote: quote_author (E.164) + quote_timestamp (ms) of the original; quote_message (quoted text; default: looked up in the local store), quote_mentions ({start, length, author}), quote_text_styles ('start:length:STYLE') and quote_attachments ('contentType[:filename[:previewFile]]') only shape the quote bubble. no_urgent=true sends without a push notification; notify_self=true delivers a normal notifying message if you are among the recipients. Contacts Signal's servers; not idempotent (repeating sends a duplicate); sends share a 20-per-minute rate limit (calls wait rather than fail). Saved to the local store (caption as body). Returns {status, timestamp}. |
| send_group_attachmentA | Send one or more files (photos, videos, documents, audio) to a Signal group in a single message. Use send_attachment for a single contact, send_group_message for text only. group_id comes from list_groups. path: a single file; paths: several files sent together (one of the two is required). Files must lie inside the allowed send folders (default: the signal-mcp attachments folder, ~/Downloads, ~/Desktop, ~/Documents; override with SIGNAL_MCP_SEND_ROOTS) and not be hidden, else an error is returned. caption: text shown with the files (default empty); view_once=true lets each member open media only once; voice_note=true marks audio as a voice note. attachment_dimensions (WIDTHxHEIGHT per file, in order) and attachment_blurhash (BlurHash per file, in order) give clients a size and a preview before download; an empty string skips a file. Reply/quote: quote_author (E.164) + quote_timestamp (ms) of the original; quote_message (quoted text; default: looked up in the local store), quote_mentions ({start, length, author}), quote_text_styles ('start:length:STYLE') and quote_attachments ('contentType[:filename[:previewFile]]') only shape the quote bubble. no_urgent=true sends without a push notification; notify_self=true delivers a normal notifying message if you are among the recipients. Contacts Signal's servers; not idempotent (repeating sends a duplicate); sends share a 20-per-minute rate limit (calls wait rather than fail). Saved to the local store (caption as body). Returns {status, timestamp}. |
| react_to_messageA | Add or remove your emoji reaction on a message in a DM or group. Use to acknowledge without replying; use send_message / send_group_message for a text reply. target_author: E.164 number of the message's sender; target_timestamp: its ms timestamp (message id in get_conversation, or a send_* result). emoji: e.g. '👍'. Give recipient (E.164) for a DM or group_id for a group; neither is an error. You have at most one reaction per message: a new emoji replaces the old one, repeating the same one changes nothing. remove=true retracts that reaction (emoji still required; default false). Contacts Signal's servers; not stored locally. Returns {status: 'reaction sent' | 'reaction removed'}. |
| set_typingA | Show or cancel the 'typing…' indicator in a DM or group. Purely cosmetic; use before send_message / send_group_message in an automated reply. recipient: E.164 number for a DM; group_id for a group (from list_groups); at least one is required. stop=true cancels an active indicator early (default false = start). The indicator expires by itself after ~15 seconds and is cleared when you send, so stop is rarely needed; one call per message is enough — don't loop. If the recipient disabled typing indicators it is silently ignored. Contacts Signal's servers; nothing is stored. Returns {status: 'typing indicator sent'}. |
| get_profileA | Check one phone number against Signal's servers and return what that lookup provides. number (required, E.164). Returns a contact object whose number and uuid (the Signal account id; null if the number is not registered) are filled; the lookup carries no profile data, so name, given_name, family_name and about come back null. For a contact's profile name and about text use find_contact or list_contacts with all_recipients=true; for their photo use get_avatar; to check many numbers at once use get_user_status. To change your own profile use update_profile. |
| block_contactA | Block a contact so you stop receiving their messages and calls. number (required, E.164). Only works when signal-mcp is the account's primary device — fails with 'This command doesn't work on linked devices' if signal-mcp was set up via signal-cli link. The contact is not notified. The blocked list is synced to your linked devices, and if you share no group with them your profile key is rotated so they lose access to your profile updates. Message history is kept. Blocking an already blocked contact is a no-op. Reversible with unblock_contact. To only delete the local contact entry without blocking, use remove_contact. Returns status 'blocked' and number. |
| unblock_contactA | Unblock a previously blocked contact so their messages and calls reach you again. number (required, E.164). Only works when signal-mcp is the account's primary device — fails with 'This command doesn't work on linked devices' if signal-mcp was set up via signal-cli link. The contact is not notified; unblocking also accepts their message request (your profile is shared with them again) and syncs to your linked devices. Unblocking a contact that is not blocked is a no-op. Use list_contacts with blocked=true to see who is blocked, block_contact to block again. Returns status 'unblocked' and number. |
| remove_contactA | Delete a contact's entry (name, nickname, note) from your contact list; synced to your linked devices. It does not block them, delete messages or stop them messaging you — use block_contact for that. number (required, E.164). hide (default false): only hide the contact from the list and keep its data. forget (default false): delete ALL data for this recipient, including identity keys and sessions — not reversible. hide and forget are mutually exclusive (error if both). Returns status 'removed' and number. To rename instead, use update_contact. |
| update_profileA | Change your own Signal profile, which is uploaded to Signal's servers and seen by people you share your profile with. Only the fields you pass change. name: display name (alias of given_name; given_name wins if both are set). given_name / family_name: the two parts of your profile name. about: bio text; about_emoji: emoji shown next to it. mobilecoin_address: base64-encoded MobileCoin public address. avatar_path: local image file (JPEG or PNG) inside the allowed send folders (SIGNAL_MCP_SEND_ROOTS), no hidden paths. remove_avatar (default false): clear the current photo. Returns status 'profile updated'. To label someone else locally use update_contact; to rename a linked device use update_device; for account settings use update_account. |
| create_groupA | Create a new Signal group with you as admin. name (required) is the group name shown to everyone; members (required) is a list of E.164 phone numbers to add; description (optional) is the group info text; avatar (optional) is a local image path, which must be inside the allowed send folders (SIGNAL_MCP_SEND_ROOTS). Contacts Signal's servers and notifies every member; not idempotent — calling twice creates two groups. Returns status, groupId (base64; use it as group_id elsewhere), timestamp and per-member send results. Use update_group to change an existing group and send_group_message to post in it. |
| join_groupA | Join a Signal group from an invite link. uri (required) is the link, https://signal.group/#... Use this for groups you are not in; for groups you already belong to use list_groups. Contacts Signal's servers and notifies the group. If the group requires admin approval you become a requesting member and the result has onlyRequested=true (or the call fails with 'Pending admin approval'); an invalid or reset link fails with 'Group link is invalid'. Returns status, groupId, timestamp and send results; use the groupId as group_id for send_group_message. |
| list_devicesA | List all devices linked to your Signal account, queried from Signal's servers. Takes no parameters. Returns entries with id, name, createdTimestamp and lastSeenTimestamp (epoch ms); device id 1 is the primary phone. Use it to audit which devices have access, or to get the device id for update_device (rename) or remove_device (unlink). |
| add_deviceA | Link a new device (e.g. Signal Desktop or another signal-cli) to your account. uri (required): the device-link URI (sgnl://linkdevice?...) shown by the new device as a QR code or printed by 'signal-cli link'. Only works when signal-mcp is the account's primary device — fails with 'This command doesn't work on linked devices' if signal-mcp was set up via signal-cli link. Also fails on a malformed or expired link or when the account already has the maximum number of linked devices. The linked device gets full access to the account; never use a URI from an untrusted source. Returns status 'device linked'; confirm with list_devices, undo with remove_device. |
| remove_deviceA | Permanently unlink a device from your Signal account; it immediately stops sending and receiving and can only come back by linking again with add_device. device_id (required, integer from list_devices) must be a linked device, not 1 (the primary). Only works when signal-mcp is the account's primary device — fails with 'This command doesn't work on linked devices' if signal-mcp was set up via signal-cli link. Returns status 'device removed' and device_id. To only rename a device, use update_device. |
| get_own_numberA | Return this server's own Signal account number as {number} (E.164, e.g. +4915112345678). Local lookup: no network call, no daemon needed, no side effects. Use it to know which messages are your own (sender == number) or to address send_note_to_self; use list_accounts instead to see every account registered in signal-cli on this machine. |
| store_statsA | Report statistics about signal-mcp's local message database (~/.local/share/signal-mcp/messages.db). Returns {total_messages, unread_messages (incoming only), db_size_bytes, oldest, newest} with oldest/newest as ISO datetimes or null when empty. Read-only, local only, no daemon needed. Use it to check whether history has been imported (import_desktop / sync_desktop) or before cleaning up with prune_store, delete_local_messages or clear_local_store. |
| get_unreadA | Return new unread incoming messages across all conversations — the default way to check for new messages. If the background service (signal-mcp install-service) is running it reads the local store; otherwise it first polls signal-cli (at most once per 30 s) and adds a _warning suggesting the service. Use get_conversation for a chat's full history, list_conversations for an inbox overview. limit: max messages (default 50, clamped 1-500); the newest are kept. Side effect: returned messages are marked read in the local store only (no read receipt — use send_read_receipt; mark_as_unread to undo). Returns {messages (oldest first; id, sender, sender_name, group_id, group_name, body, timestamp, attachments …), has_more}; if has_more is true call again with the same limit to get the next batch. |
| import_desktopA | Import the full message history from Signal Desktop on this machine into signal-mcp's local store, so get_conversation, search_messages and export_messages can see older messages. No parameters. Reads Signal Desktop's encrypted database, which needs sqlcipher installed and the database key: on macOS read from the Keychain ('Signal Safe Storage', may prompt for access), on Linux via secret-tool (libsecret / GNOME Keyring). Signal Desktop must be installed and opened at least once. Only writes to the local store; nothing is sent to Signal. Already stored messages are skipped, so re-running is safe but slow. Only one import can run at a time. Returns {imported, skipped, total, max_ts_ms, platform, source}. Use sync_desktop for later updates; it only reads messages newer than the last sync. |
| sync_desktopA | Incrementally import new messages from Signal Desktop into signal-mcp's local store: only messages newer than the last sync (with a 60-second overlap) are read, so repeat calls are fast. No parameters. The first call imports everything, like import_desktop. Same requirements as import_desktop: sqlcipher, Signal Desktop installed, and Keychain (macOS) or secret-tool (Linux) access to its key. Only writes to the local store; duplicates are skipped. Returns the import_desktop fields {imported, skipped, total, max_ts_ms, platform, source} plus since (ISO datetime of the lower bound, null on first run) and incremental (boolean). Use import_desktop only for a deliberate full re-scan. |
| list_conversationsA | List every conversation (direct and group) in the local store, most recent first — an inbox overview. Takes no parameters. Use get_conversation to read one, get_unread for only new messages, search_messages to find text. Reads only locally stored messages (no Signal server call; nothing marked read); names come from the cached signal-cli contacts and groups. Returns a list of {id (E.164 number or group_id — pass to get_conversation), type ('direct' | 'group'), name, last_message, last_message_at (ISO), message_count, unread_count}. |
| get_user_statusA | Check whether phone numbers and/or usernames are registered on Signal, in one batch query to Signal's servers. recipients: list of E.164 phone numbers; usernames: list of Signal usernames or username links; at least one of the two is required. Returns one entry per input with recipient, number or username, uuid (null if not registered) and isRegistered. Use it before messaging an unknown number; numbers that hide their discoverability can show as unregistered. For a contact's name or details use find_contact or get_profile. |
| send_sync_requestA | Ask your primary Signal device to send this linked device its contacts, groups and settings. Takes no parameters. Use it when list_contacts or list_groups is missing entries that exist on your phone; it is meant for linked setups. Asynchronous: the call returns status 'sync requested' at once and the data arrives over the next seconds through the normal receive loop. It does not fetch new messages — use receive_messages for that. To push your contacts the other way, use send_contacts_sync. |
| delete_messageA | Remote-delete (unsend) a message you sent to one contact, removing it for everyone in the chat on Signal 5.0+ clients. Use delete_group_message for groups; delete_local_messages to remove messages only from the local store. Only your own messages can be deleted — you cannot delete what others sent. recipient: E.164 number of the contact; target_timestamp: ms timestamp of your message (from the send_message result or get_conversation). Irreversible; older clients may silently ignore it; repeating it has no further effect. The local store copy is kept. Returns {status: 'deleted'}. |
| delete_group_messageA | Remote-delete (unsend) a message you sent to a group, removing it for all members on Signal 5.0+ clients. Use delete_message for DMs; admin_delete_message to delete another member's message as admin; delete_local_messages for local-only removal. group_id: from list_groups; target_timestamp: ms timestamp of your message (from the send_group_message result or get_conversation). Irreversible; older clients may silently ignore it; repeating it has no further effect. The local store copy is kept. Returns {status: 'deleted'}. |
| send_read_receiptA | Send a read receipt to a contact so their Signal app shows your messages as 'Read', and mark those messages read in the local store. Use after reading a DM with get_conversation (which marks read locally but sends no receipt). Not for groups. sender: E.164 number of the contact who sent the messages. timestamps: list of their messages' ms timestamps (the message id in get_conversation), batched in one call. Only shown if the sender has read receipts enabled. Contacts Signal's servers; repeating is harmless. Returns {status: 'read receipt sent'}. |
| update_contactA | Set your private local name, nickname or note for another contact; it is synced to your own linked devices and never shown to the contact. To change your own public profile use update_profile; to block or delete a contact use block_contact or remove_contact. number (required, E.164). Pass at least one of: name (full display name; stored as given name and clears the family name unless family_name is also given), given_name, family_name, nick_given_name, nick_family_name (nickname shown instead of their profile name), note (private note). Fails if none is given or the number is not registered on Signal. Returns status, number and name. For a disappearing-message timer use set_expiration_timer. |
| update_groupA | Change an existing group's details, membership, admins, invite link, permissions or timer; only the fields you pass change. group_id (required, from list_groups). name, description: new text. avatar: local image path inside the allowed send folders. add_members / remove_members / add_admins / remove_admins / ban_members / unban_members: lists of E.164 numbers. expiration_seconds: disappearing-message timer, 0 disables. link_mode: 'enabled' (anyone with the link joins), 'enabled-with-approval', 'disabled', or 'reset' (same as reset_link=true, which issues a new link and invalidates the old one). permission_add_member, permission_edit_details, permission_send_messages: 'every-member' or 'only-admins' (only-admins sending = announcement group). member_label / member_label_emoji set ONLY YOUR OWN label in this group (the tag next to your name); you cannot set another member's label. Changes apply immediately and every member gets a group update; removals and bans are not undone automatically. Admin rights are needed for most changes, depending on the group's permissions. Returns status and group_id. Use send_group_message to post, leave_group to exit. |
| leave_groupA | Leave a Signal group yourself: sends a quit message to all members and removes you from the member list; the group continues for everyone else. To end the group for all members use terminate_group instead. group_id (required, from list_groups). admins: E.164 numbers of members to promote first — if you are the group's ONLY admin you must name one, otherwise signal-cli refuses. delete (default false): also delete the group's local data after leaving. You can only come back by being re-added or via an invite link. Returns status and group_id. |
| terminate_groupA | DESTRUCTIVE AND IRREVERSIBLE: permanently terminate a Signal group FOR ALL MEMBERS; afterwards nobody can send messages or start calls in it. Requires admin rights. To just exit the group yourself, use leave_group. group_id (required, from list_groups). confirm (required) must be true, otherwise nothing happens and an error is returned; only set it after the user explicitly asked to end the group for everyone. Returns status and group_id. |
| pin_messageA | Pin a message at the top of a DM or group conversation for all participants. Pinning is visible to everyone — don't use it as a private bookmark (send_note_to_self instead). Use unpin_message to remove a pin. target_author: E.164 number of the message's sender; target_timestamp: its ms timestamp (message id in get_conversation). Give recipient (E.164) for a DM or group_id for a group; neither is an error. Contacts Signal's servers; pinning an already pinned message changes nothing. Returns {status: 'message pinned'}. |
| unpin_messageA | Remove a pinned message from the top of a DM or group conversation for all participants; the message itself stays. Use pin_message to pin. target_author: E.164 number of the pinned message's sender; target_timestamp: its ms timestamp (message id in get_conversation). Give recipient (E.164) for a DM or group_id for a group; neither is an error. Contacts Signal's servers; repeating has no further effect. Returns {status: 'message unpinned'}. |
| admin_delete_messageA | As a group admin, delete any member's message in that group for all participants. For your own messages use delete_group_message (group) or delete_message (DM); for local-only removal delete_local_messages. Requires admin rights — check is_admin in list_groups. group_id: from list_groups; target_author: E.164 number of the message's sender; target_timestamp: its ms timestamp (message id in get_conversation). Irreversible; contacts Signal's servers; the local store copy is kept. Returns {status: 'message deleted by admin'}. |
| send_contacts_syncA | Send your local contact list to your other linked devices as a sync message, one-way from this device outward. Takes no parameters. Use it when contacts added or renamed through signal-mcp do not appear on your phone or desktop. Contacts no one else and changes nothing locally. Returns status 'contacts synced to linked devices'. To pull data from the primary device instead, use send_sync_request. |
| update_deviceA | Rename a device on your Signal account; the name shows in every device's Linked Devices list. device_id (required, integer from list_devices); name (required): the new label. Renaming the device signal-mcp itself runs on works everywhere; renaming any other device only works when signal-mcp is the account's primary device — otherwise it fails with 'This command doesn't work on linked devices'. Does not affect messaging. Returns status, device_id and name. To unlink a device use remove_device; to change your profile name use update_profile. |
| mark_as_unreadA | Mark messages as unread again in the local signal-mcp store, so get_unread returns them on its next call — e.g. to flag for follow-up. Local only: read receipts already sent and other devices are unaffected (send_read_receipt is the opposite, outward action). message_ids: list of message id strings exactly as returned by get_conversation, get_unread or search_messages; unknown ids are ignored. Idempotent. Returns {status, count} where count is the number of ids passed. |
| get_avatarA | Return a contact's or group's avatar image as base64, from the copy signal-cli has stored. identifier (required): an E.164 phone number for a contact, or a group id from list_groups for a group (anything that is not a full E.164 number is treated as a group id). Returns identifier, base64 (decode to get the JPEG/PNG bytes) and has_avatar; fails with 'Could not find avatar' when none is stored. Use update_profile with avatar_path to set your own photo, update_group with avatar for a group's. |
| send_message_request_responseA | Accept or decline a message request from someone not in your contacts. sender (required, E.164) is who sent the request; accept (required): true accepts, which shares your profile with them; false declines (deletes the request) and turns profile sharing off. Declining does not block them — use block_contact for that. The decision is recorded locally and synced to your linked devices; the sender is not sent a message. Returns status 'message request accepted' or 'message request declined' and sender. |
| create_pollA | Create a poll and send it to a contact or group. Use vote_poll to vote and terminate_poll to close it. question: the poll text; options: answer strings (at least 2, else an error); multi_select=true lets voters pick several answers (default false = single choice). Give recipient (E.164) for a DM or group_id (from list_groups) for a group; neither is an error. Contacts Signal's servers; not idempotent (each call sends a new poll); shares the 20-sends-per-minute rate limit; not saved to the local store. Returns {status: 'poll created', timestamp} — the timestamp (with your number as author) identifies the poll for vote_poll and terminate_poll. |
| vote_pollA | Cast or change your vote on an open Signal poll in a DM or group; the vote is visible to participants. Use create_poll to start a poll, terminate_poll to close your own. A poll has no separate id: identify it by target_author (E.164 number of the poll's creator) + target_timestamp (ms timestamp of the poll message, from create_poll or get_conversation). votes: 0-based indices into the poll's options — exactly one for a single-choice poll, all chosen indices at once for multi-select (each call replaces your previous vote). Give recipient (E.164) for a DM poll or group_id for a group poll; neither is an error. Voting on a terminated poll fails. Contacts Signal's servers; a local per-poll counter is incremented so re-votes supersede earlier ones. Returns {status: 'vote sent'}. |
| terminate_pollA | Close a poll you created so no more votes are accepted; participants see it as ended with final results. Irreversible. Use vote_poll to vote, create_poll to start a new one. Only the creator can terminate. target_author: your own E.164 number (accepted for symmetry with vote_poll; only the timestamp is sent); target_timestamp: ms timestamp of the poll (from create_poll or get_conversation). Give recipient (E.164) for a DM poll or group_id for a group poll; neither is an error. Contacts Signal's servers. Returns {status: 'poll terminated'}. |
| set_expiration_timerA | Set or turn off the disappearing-messages timer of a one-to-one or group chat. expiration_seconds (required): lifetime of new messages in seconds, 0 disables; common values 3600 (1h), 86400 (1d), 604800 (1w), 2592000 (30d). Pass recipient (E.164) for a direct chat or group_id (from list_groups) for a group; one is required, and group_id wins if both are given. Every participant is notified and the timer applies to new messages; already sent messages are unaffected. For a group this is the same as update_group with expiration_seconds. Returns status and seconds. |
| list_identitiesA | List stored Signal identity keys (safety numbers) and their trust levels, from the local store. number (optional, E.164) limits the result to one contact; omit it for all. Returns entries with number, uuid, fingerprint, safetyNumber, scannableSafetyNumber, trustLevel and addedTimestamp (epoch ms). trustLevel is TRUSTED_VERIFIED (manually verified), TRUSTED_UNVERIFIED (trusted on first use) or UNTRUSTED (key changed; sending is blocked until re-trusted). Use it when Signal reports 'safety number changed' or before trust_identity; it does not change trust itself. |
| trust_identityA | Mark a contact's identity key as trusted so sending to them works again after their safety number changed (e.g. they reinstalled Signal). number (required, E.164). safety_number (optional): the safety number or fingerprint you verified in person or by call, as shown by list_identities; only that key is trusted and it becomes TRUSTED_VERIFIED. Without safety_number ALL known keys for the number are trusted unverified — this unblocks delivery but skips verification, so prefer passing it. Changes local trust only; the contact is not notified. Fails if the number or safety number does not match. Returns status 'trusted' and number. |
| clear_local_storeA | Delete ALL messages and attachment records from signal-mcp's local database. confirm (boolean, required) must be exactly true, otherwise nothing is deleted and an error is returned. Local only: nothing is deleted from Signal, your phone or other devices, and downloaded attachment files on disk are left in place. Irreversible except by re-importing (import_desktop) or receiving again. Returns {deleted: count, status: 'cleared'}. Use delete_local_messages to clear one conversation, prune_store to drop only old messages; delete_message unsends a message in Signal. |
| delete_local_messagesA | Delete the locally stored messages of one conversation from signal-mcp's database. recipient (required): the contact's E.164 phone number or the group ID; your own number deletes only your note-to-self messages. Local only: nothing is unsent from Signal or removed from other devices; irreversible locally. Returns {deleted: count, status: 'deleted'} (0 if nothing matched). Use clear_local_store to wipe everything, prune_store to drop messages by age, delete_message to unsend in Signal. |
| export_messagesA | Export messages from signal-mcp's local store as one JSON or CSV string, for archiving or analysis. format: 'json' (default; full message objects with resolved sender_name/group_name, attachments and extras) or 'csv' (flat columns id, timestamp, sender, sender_name, recipient, group_id, group_name, body, quote_id, is_read). recipient (optional): limit to one conversation, an E.164 phone number or a group ID. since (optional, ISO 8601 datetime, e.g. '2026-01-01T00:00:00'): only messages at or after it; an invalid value returns an error. Read-only. Only messages already in the local store are included (run sync_desktop or receive_messages first). Returns {format, data}. Use get_conversation or search_messages to read messages interactively. |
| update_configurationA | Change account-wide messaging settings and sync them to your linked devices. All four booleans are optional; omit any you do not want to change, and a call with none is a no-op: read_receipts whether senders are told when you have read their messages; typing_indicators whether contacts see you typing; link_previews whether URLs in outgoing messages get previews; unidentified_delivery_indicators whether sealed-sender delivery icons are shown. Primary device only: on a linked signal-cli setup it fails with 'This command doesn't work on linked devices'. Returns {status: 'updated'}. signal-cli cannot read the current values back, so track what you set. Use update_account for discoverability, number sharing and username; update_profile for name and avatar. |
| list_sticker_packsA | List the sticker packs installed on this Signal account. No parameters. Returns signal-cli's array of packs: {packId (hex), url, installed, title, author, cover, stickers: [{id, emoji, contentType}]}. Read-only. Use packId and a sticker id with send_sticker, send_group_sticker or get_sticker. Use add_sticker_pack to install a pack from a signal.art link. |
| add_sticker_packA | Install an existing sticker pack on this Signal account from its signal.art link. uri (required): https://signal.art/addstickers/#pack_id=&pack_key=; both pack_id and pack_key are needed. Installing the same pack again has no further effect. Returns {status: 'installed', pack_id} with pack_id parsed from the uri, ready for send_sticker, send_group_sticker or get_sticker; call list_sticker_packs to see the sticker ids and emoji in it. Use upload_sticker_pack instead to publish a new pack of your own images. |
| send_stickerA | Send one sticker from an installed sticker pack to a single contact; it renders as a sticker, not a file. Use send_group_sticker for groups, send_attachment for ordinary images. recipient: E.164 number. pack_id: hex pack id and sticker_id: integer index within the pack, both from list_sticker_packs; an uninstalled pack or invalid id returns an error — install packs first with add_sticker_pack (signal.art URL). Contacts Signal's servers; not idempotent (repeating sends a duplicate); sends share a 20-per-minute rate limit (calls wait rather than fail). Saved locally as '[sticker pack:id]'. Returns {status, timestamp}. |
| send_group_stickerA | Send one sticker from an installed sticker pack to a Signal group; it renders as a sticker, not a file. Use send_sticker for a single contact, send_group_attachment for ordinary images. group_id: from list_groups. pack_id: hex pack id and sticker_id: integer index within the pack, both from list_sticker_packs; an uninstalled pack or invalid id returns an error — install packs first with add_sticker_pack (signal.art URL). Contacts Signal's servers; not idempotent (repeating sends a duplicate); sends share a 20-per-minute rate limit (calls wait rather than fail). Saved locally as '[sticker pack:id]'. Returns {status, timestamp}. |
| send_storyA | Post an image or video as a Signal story, visible to its audience for 24 hours. Use send_message / send_attachment to message someone directly. path: one local image or video file; Files must lie inside the allowed send folders (default: the signal-mcp attachments folder, ~/Downloads, ~/Desktop, ~/Documents; override with SIGNAL_MCP_SEND_ROOTS) and not be hidden, else an error is returned. group_id (from list_groups) posts to that group's story instead of My Story. allow_replies (default true): false disables replies. Contacts Signal's servers; not idempotent (each call posts a new story); shares the 20-sends-per-minute rate limit; not saved to the local store. Returns {status: 'posted', timestamp}. |
| list_attachmentsA | List the attachment files saved in the local attachments folder (~/Downloads/signal-attachments). No parameters. Returns an array of {filename, path, size (bytes), modified (ISO datetime)}, sorted by filename; empty if the folder does not exist. Read-only and local; nothing is downloaded. Pass a filename to get_attachment for its details. Attachments of incoming messages land here when messages are received (receive_messages or the background service). |
| get_attachmentA | Look up one received attachment and make sure it is saved locally. filename (required): a filename from list_attachments, or a signal-cli attachment id from a message's attachments; path components such as '../' are rejected. If the file is not in ~/Downloads/signal-attachments, it is copied there from signal-cli's own attachment store (only attachments signal-cli already downloaded; otherwise 'Attachment not found'). Returns {filename, path, size (bytes), modified (ISO datetime)}: metadata and the local path, not the file content. Nothing is sent to Signal. Use send_attachment or send_group_attachment to send a file. |
| get_stickerA | Fetch one sticker image from an installed pack as base64. pack_id (required, hex string) and sticker_id (required, integer) come from list_sticker_packs (packId and stickers[].id) or add_sticker_pack. Read-only. Returns {base64}; the image format is the sticker's contentType from list_sticker_packs (usually image/webp). Use send_sticker or send_group_sticker to send it instead of downloading it. |
| upload_sticker_packA | Publish a new sticker pack made from your own images to Signal's servers and get a shareable signal.art link. path (required): local path to a manifest.json (with the sticker images next to it) or to a zip containing the manifest and images. The file must be inside an allowed folder (~/Downloads/signal-attachments, ~/Downloads, ~/Desktop, ~/Documents, or the SIGNAL_MCP_SEND_ROOTS list) and not in a hidden folder. The pack is public to anyone with the link and cannot be deleted through this tool. Invalid packs or oversized images return an error. Returns {url}. Use add_sticker_pack to install an existing pack. |
| list_accountsA | List every Signal account registered in signal-cli on this machine. Returns a JSON array of E.164 phone numbers (e.g. ["+4915112345678"]); no registration status or other fields. Read-only, asks the running signal-cli daemon. Most setups have exactly one account. Use get_own_number instead to get the single account this server sends and receives as. |
| update_accountA | Change account attributes stored on Signal's servers for this account. All six parameters are optional and only the ones you pass are sent: device_name (string) renames this device as shown in list_devices on your other devices; discoverable_by_number (boolean) whether people who have your number can find you on Signal; number_sharing (boolean) whether your phone number is shown to people you message; unrestricted_unidentified_sender (boolean) true lets anyone, not only contacts, send you sealed-sender messages; username (string, without @) claims a Signal username; delete_username (boolean) removes the current one and takes precedence over username if both are given. Takes effect immediately on the real account; setting a username that is taken or invalid fails with an error. Returns {status: 'account updated'} (the resulting username is not echoed). Use update_configuration for read receipts, typing indicators and link previews, update_profile for your name, about text and avatar. |
| set_pinA | Set or replace the Registration Lock PIN on your Signal account, so re-registering your phone number elsewhere (e.g. after a SIM swap) requires this PIN. pin (string, required): the new PIN, numeric, e.g. '123456'. Primary device only: on a linked signal-cli setup it fails with 'This command doesn't work on linked devices'. Affects the real account immediately; calling again with a new pin replaces the old one. If you forget it, re-registration is blocked until the lock lapses after 7 days of inactivity. Returns {status: 'PIN set'}. Use remove_pin to turn the lock off; finish_change_number needs this PIN when changing numbers. |
| remove_pinA | Remove the Registration Lock PIN from your Signal account, so anyone who controls your phone number can re-register it without a PIN. No parameters. Primary device only: on a linked signal-cli setup it fails with 'This command doesn't work on linked devices'. Affects the real account immediately; undo it by calling set_pin again. Returns {status: 'PIN removed'}. Use set_pin to change the PIN instead of removing it. |
| start_change_numberA | Step 1 of moving this Signal account to a new phone number: asks Signal to send a verification code to the new number. number (required): the new number in E.164 format, e.g. +12025551234; voice (boolean, default false): deliver the code by voice call instead of SMS; captcha (optional): a captcha token, needed only when a previous attempt failed with a captcha-required error; solve one at https://signalcaptchas.org/registration/generate.html and pass the resulting signalcaptcha:// token. Primary device only: on a linked signal-cli setup it fails with 'This command doesn't work on linked devices'. The account stays on the old number until finish_change_number succeeds. Rate limits are returned as errors. Returns {status: 'verification code sent', number}. Then call finish_change_number with the same number and the received code. |
| finish_change_numberA | Step 2 of moving this Signal account to a new phone number: submits the verification code and completes the change. Call start_change_number first; without it there is no code to verify. number (required): the same new E.164 number given to start_change_number; verification_code (required): the 6-digit code received by SMS or voice call; pin (optional): your Registration Lock PIN, needed only if one is set (see set_pin). Primary device only: on a linked signal-cli setup it fails with 'This command doesn't work on linked devices'. On success the real account is moved to the new number; this cannot be undone except by another change-number round. A wrong pin fails with the number of tries remaining. Returns {status: 'number changed', number}. |
| submit_rate_limit_challengeA | Lift a Signal rate limit on sending by submitting a proof-of-humanity challenge. Use it when a send fails with a rate-limit / proof-required error that includes a challenge token. challenge (required): the challenge token from that error; captcha (required): the token from solving the captcha at https://signalcaptchas.org/challenge/generate.html. Talks to Signal's servers; a rejected captcha returns an error and you need a fresh one. Returns {status: 'challenge submitted'}; then retry the failed send. For a captcha needed while changing number, pass it to start_change_number instead. |
| prune_storeA | Delete locally stored messages older than a number of days from signal-mcp's database, together with their attachment records and search index entries. days (integer, default 180, must be positive): messages with a timestamp older than now minus this many days are deleted. Local only: nothing is deleted from Signal; irreversible locally. Returns {deleted: count, older_than_days}. Use it to keep the store small; use delete_local_messages for one conversation or clear_local_store to wipe everything. |
| set_webhookA | Save or clear the webhook URL that signal-mcp's background receiver (signal-mcp receive --watch, run by install-service) POSTs every incoming message to. url (optional): an http(s) URL such as 'http://localhost:5678/webhook/signal'; omit it or pass null/empty to clear. Stored in ~/.local/share/signal-mcp/webhook.json; the receiver reads it at startup, so restart the service for a change to apply. The SIGNAL_MCP_WEBHOOK environment variable overrides it. Payload: JSON {event: 'message', timestamp, sender, recipient, group_id, body, quote_id, attachments, is_read, receipt_type, expires_in_seconds, view_once}. Returns {status: 'webhook set', url} or {status: 'webhook cleared'}. Use get_webhook to check the value in effect. |
| get_webhookA | Return the webhook URL in effect as {url}, or {url: null} if none is configured. No parameters. The SIGNAL_MCP_WEBHOOK environment variable wins over the URL saved with set_webhook. Read-only, local only. Use set_webhook to change or clear it. |
| find_contactA | Find contacts whose number, name, given/family name, nickname or username contains query (case-insensitive substring; required). Use it to resolve a name to an E.164 number before send_message, or to check that a contact exists; use list_contacts instead to list everyone or filter by blocked status. all_recipients (default false) also searches people outside your address book, e.g. group members known only by their profile name. Reads the local contact store only. Returns a list (possibly empty) of contact objects with number, uuid, name, given_name, family_name, about, blocked and display_name. |
| schedule_messageA | Save a text message in the local store to be sent at a future time. Give exactly one target: recipient (E.164 phone number, for a direct message) or group_id (for a group). message (required): the text. send_at (required): local time as 'YYYY-MM-DDTHH:MM[:SS]' or 'YYYY-MM-DD HH:MM[:SS]' (no timezone); must be in the future. Nothing is sent at that time by itself: a due message goes out only when run_scheduled_messages (or the |
| list_scheduled_messagesA | List messages scheduled with schedule_message, ordered by send time. include_done (boolean, default false): also include jobs that were sent, cancelled or failed; by default only pending jobs are listed. Read-only, local only. Returns an array of {id, recipient, group_id, message, send_at, created_at, status ('pending'|'sent'|'cancelled'|'failed'), error}. Use the id with cancel_scheduled_message; run_scheduled_messages sends the pending jobs that are due. |
| cancel_scheduled_messageA | Cancel one pending scheduled message so it will never be sent. job_id (required, integer): the id from schedule_message or list_scheduled_messages. Local only; the job stays in the list with status 'cancelled' and cannot be re-activated (schedule it again instead). Returns {status: 'cancelled', job_id}, or an error if no pending job has that id (already sent, failed, cancelled or unknown). |
| run_scheduled_messagesA | Send every scheduled message whose send_at time has passed and is still pending. No parameters. Nothing else delivers scheduled messages, so call this (or run |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 81 tools
Each tool's description is exceptionally cross-referenced, but the surface is crowded with near-parallel variants that could be confused: send_message / send_group_message / send_attachment / send_group_attachment / send_note_to_self / send_sticker / send_group_sticker all mean 'send something', and delete_message / delete_group_message / admin_delete_message / delete_local_messages / clear_local_store / prune_store form a dense deletion cluster. The receive trio (receive_messages / receive_direct / get_unread) and the set_expiration_timer vs update_group(expiration_seconds) overlap add further selection burden. Boundaries are documented, but the agent must read carefully to pick correctly.
Names follow a strictly consistent snake_case verb_noun pattern (list_contacts, send_group_message, update_group, terminate_poll, remove_device, prune_store). Read and write operations are clearly signaled by their verb prefixes throughout all 81 tools with no mixed conventions.
81 tools is far beyond a comfortable surface for an agent. While the domain is genuinely broad (messaging, groups, devices, account, stickers, polls, stories, scheduling, local store), many tools are one-shot admin operations (set_pin, submit_rate_limit_challenge, finish_change_number, upload_sticker_pack) or parameterizable variants that could be collapsed, making the set heavy and hard to navigate.
Coverage is exhaustive: full messaging lifecycle (send/edit/react/pin/delete/read-receipt), group and membership administration, contacts, profiles, devices, identity/trust, polls, stories, stickers, scheduling, and local-store import/export/prune management. There are essentially no dead ends for the stated purpose of controlling a full Signal account.