Skip to main content
Glama
Sealjay

mcp-signal

by Sealjay

mcp-signal

Python uv MCP License: MIT GitHub issues GitHub stars Sealjay/mcp-signal MCP server

A local Model Context Protocol (MCP) server that reads Signal Desktop history from the local encrypted database via signal-export and sends outbound messages via signal-cli.

mcp-signal focuses on the core workflow for personal Signal automation — list chats, read messages, search messages, inspect groups, and send messages to direct or group chats. Everything runs locally; stdio transport by default (no network listener), with an optional HTTP mode available.

Heads up — mixed backend. Read/search comes from the local Signal Desktop database. Sending uses signal-cli, which must be installed and linked to a Signal account separately. If signal-cli is unavailable, read/search still works but send tools do not.

Features

  • List direct and group chats from Signal Desktop

  • Read recent messages from a chat

  • Search messages within one chat or across all chats

  • List group chats with signal-cli group IDs for outbound use

  • Send a message to:

    • a direct recipient by phone number

    • a group by group ID

    • a chat by exact chat name (with ambiguity checks)

  • Runs entirely on your machine; stdio transport by default (no network listener), with an optional HTTP mode

Related MCP server: Signal MCP

Setup

Prerequisites

  • Python 3.12+

  • uv

  • Signal Desktop with an existing local message database

  • signal-cli installed and linked if you want outbound sends

Installation

  1. Clone this repository

    git clone https://github.com/Sealjay/mcp-signal.git
    cd mcp-signal
  2. Install dependencies

    uv sync
  3. Install signal-cli (optional — only needed for outbound sends)

    On macOS, the simplest route is Homebrew:

    brew install signal-cli

    Tested with, and recommended: signal-cli 0.14.8 (signal-cli --version). Older releases may work but are not tested.

Configure outbound sends

The server auto-loads a local .env.local file from the repo root if present. This file is gitignored and is the recommended place for machine-local config.

cat > .env.local <<'EOF'
SIGNAL_ACCOUNT="+441234567890"
EOF

Optional environment variables:

Variable

Purpose

SIGNAL_CLI_PATH

Override the signal-cli binary path

SIGNAL_DATA_DIR

Override the Signal Desktop data directory

SIGNAL_DB_PASSWORD

Password for encrypted desktop DBs if needed

SIGNAL_DB_KEY

Raw key for encrypted desktop DBs if needed

SIGNAL_JSONRPC_TIMEOUT_SECONDS

Timeout for signal-cli JSON-RPC calls, in seconds. Defaults to 30, capped at 300

MCP_AUTH_TOKEN

Bearer token required on HTTP requests except /health. Unset means no auth

MCP_LISTEN_ADDR

host:port to bind in HTTP mode. Defaults to 0.0.0.0:8765; setting it enables HTTP mode

Environment variables set in the shell take precedence over .env.local.

MCP_* variables must be set in the shell/environment, not .env.local — the local env loader only picks up SIGNAL_-prefixed keys from that file.

mcp-signal does not manage linking itself. Link the local signal-cli device first:

signal-cli link -n "signal-mcp"

Scan the QR code in the Signal mobile app (Settings → Linked Devices → Link New Device).

Do not pass -a / --account to link on current signal-cli versions — linking a new secondary device does not take a phone number there.

After the QR is accepted, confirm the linked account is visible:

signal-cli listAccounts

That account should match the SIGNAL_ACCOUNT value in .env.local.

signal-cli stores its linked-account state under its own local data directory (typically ~/.local/share/signal-cli/data on macOS/Linux). That state lives outside this repository and is not committed by mcp-signal.

Verify everything is connected:

uv run signal-mcp smoke

HTTP mode (optional)

By default signal-mcp serve runs over stdio. To serve over streamable HTTP instead, pass --http (optionally with --listen-addr), or set MCP_LISTEN_ADDR in the environment:

uv run signal-mcp serve --http --listen-addr 0.0.0.0:8765

Set MCP_AUTH_TOKEN to require a bearer token on all HTTP routes except /health.

MCP client configuration

All clients launch the server the same way over stdio. On macOS, you may need the absolute path to uv — see macOS: uv PATH below.

Claude Code

The quickest route is the CLI:

claude mcp add --transport stdio signal --scope user -- uv run --directory /absolute/path/to/mcp-signal signal-mcp serve

Alternatively, add to .mcp.json at your project root (or ~/.claude.json for a user-scoped server):

{
  "mcpServers": {
    "signal": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-signal", "signal-mcp", "serve"]
    }
  }
}

If you edit the file directly, restart the Claude Code session to pick it up.

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):

{
  "mcpServers": {
    "signal": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-signal", "signal-mcp", "serve"]
    }
  }
}

Restart Claude Desktop. You should see signal listed as an available integration.

Cursor

Add to ~/.cursor/mcp.json:

{
  "mcpServers": {
    "signal": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-signal", "signal-mcp", "serve"]
    }
  }
}

Restart Cursor.

macOS: uv PATH

GUI apps (Claude Desktop, Cursor) don't always inherit the PATH from your interactive terminal, so uv may fail with spawn uv ENOENT. Fix by using the absolute path to uv in command:

  • Homebrew — /opt/homebrew/bin/uv (Apple Silicon) or /usr/local/bin/uv (Intel)

  • Manual install — run which uv in your terminal to find it

Example:

{
  "mcpServers": {
    "signal": {
      "command": "/opt/homebrew/bin/uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-signal", "signal-mcp", "serve"]
    }
  }
}

Architecture

Component

Description

MCP server

Python/FastMCP, stdio transport

Read path

signal-export reading the local Signal Desktop database

Send path

signal-cli JSON-RPC launched on demand

State

No separate cache; reads directly from Signal Desktop data

Data flow

  1. The MCP client launches signal-mcp serve over stdio.

  2. Read/search tools call signal-export against the local Signal Desktop database.

  3. Group listing and outbound sends call signal-cli -a ACCOUNT jsonRpc.

  4. Results are returned as structured JSON.

Project structure

mcp-signal/
  src/mcp_signal/
    config.py
    main.py
    reader.py
    server.py
    signal_cli.py
  tests/
  CLAUDE.md
  LICENSE
  README.md
  SECURITY.md

Tools

Tool

Purpose

list_chats

List direct and group chats from Signal Desktop

read_messages

Read messages from a specific chat

search_messages

Search messages within one chat or across all chats

list_groups

List groups from signal-cli, including group IDs

chat_activity

List chats ranked by recent activity with last-message/last-reply dates and unanswered-inbound counts

decrypt_attachment

Decrypt a locally stored Signal attachment and return the path to the decrypted file

send_message

Send a text message to a direct recipient or group. chat_name resolves to a phone number, or to the contact's service ID when their number is hidden

get_status

Show desktop DB / signal-cli / account readiness

pairing_status

Report signal-cli device-link setup state and surface the live link QR for first-run pairing

Prompts: summarise_chat (summarise one chat) and discover_topics (recurring topics across chats or within one).

Privacy and security

  • No cloud relay. stdio transport by default (no network listener); optional HTTP mode with bearer auth available. All data stays on your machine.

  • Read/search uses your local Signal Desktop data only.

  • Send operations require a locally configured signal-cli account.

  • .env.local is intended for local secrets such as SIGNAL_ACCOUNT and is not committed.

  • signal-cli linked-device state is stored in its own local app data directory, outside this repo, and is not committed.

See SECURITY.md for how to report vulnerabilities.

Limitations

  • Prompt-injection risk: as with many MCP servers, this one is subject to the lethal trifecta. Malicious incoming messages could attempt to instruct an agent to exfiltrate other messages. Treat the tool surface accordingly and review outbound actions before approving them.

  • Mixed backend: chat history comes from Signal Desktop, while outbound sends come from signal-cli.

  • No attachments: text-only send.

  • No real-time notifications: polling/read only.

  • Read metadata comes from Signal Desktop, not signal-cli: reads use the Signal Desktop database, so signal-cli 0.14.8's data-message metadata is not surfaced. Voice notes are flagged as is_voice_note on each attachment, using Desktop's own attachment flag.

  • Single account per MCP instance.

  • Group sends need signal-cli: local DB reads alone do not provide enough information to send to groups safely.

Development

uv sync
uv run signal-mcp smoke
uv run pytest
uv run ruff check .

Troubleshooting

  • signal-cli not found — confirm signal-cli is on PATH or set SIGNAL_CLI_PATH in .env.local. On macOS, brew install signal-cli is the simplest route.

  • Read/search works but sends fail — signal-cli is not linked or SIGNAL_ACCOUNT is not set. Run signal-cli listAccounts to verify, then check .env.local.

  • Sends fail with [403] Authorization failed — the linked signal-cli device was removed or expired. Run signal-cli listAccounts; if it shows this error, re-link with signal-cli link -n "signal-mcp".

  • signal-cli link hangs or fails — do not pass -a / --account to link on current versions. Run signal-cli link -n "signal-mcp" and scan the QR from your phone.

  • MCP client can't launch the server — args must contain an absolute path to the repo, not relative. If uv itself fails with spawn uv ENOENT, see macOS: uv PATH.

  • No messages returned — confirm Signal Desktop is installed and has message history. The read path queries the local Signal Desktop database directly.

Contributing

Contributions welcome via pull request. Please:

  • Run uv run ruff check . before pushing.

  • Ensure uv run pytest passes.

See CLAUDE.md for the full development workflow.

Licence

MIT Licence — see LICENSE.

Available Tools

9 tools
chat_activityA
Read-onlyIdempotent

List Signal chats ranked by recent activity, showing last message date, last reply date, and count of unanswered inbound messages.

Read-only with no side effects. Use this to identify chats that need a response. Use list_chats instead for a general chat directory with message previews.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of chats to return, between 1 and 200. Defaults to 50.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true, so 'Read-only with no side effects' restates structured data rather than adding to it. The ranking/derived-metric behavior is useful context, but no new behavioral traits (ordering guarantees, staleness, empty-result behavior) are disclosed beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short blocks, front-loaded with the purpose and followed by routing guidance; nothing is bloated. The only minor waste is the read-only sentence, which duplicates the annotations and could be dropped without losing information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be re-explained, and the description covers purpose, ranking semantics, usage intent, and the sibling alternative. For a single-optional-parameter read tool, nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single limit parameter is fully documented with range and default in the schema. The description contributes no additional meaning about pagination, defaulting, or how limit interacts with ranking, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb (List), resource (Signal chats), and scope (ranked by recent activity), plus the exact fields surfaced: last message date, last reply date, and count of unanswered inbound messages. It also explicitly distinguishes itself from list_chats, which allows an agent to route correctly without inspecting either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the intended task ('Use this to identify chats that need a response') and names the alternative plus the condition that selects it ('Use list_chats instead for a general chat directory with message previews'). Both the when-to-use and the when-to-use-something-else paths are explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

decrypt_attachmentA

Decrypt a locally stored Signal attachment and return the path to the decrypted file.

The encrypted_path and local_key values come from attachment metadata in read_messages or search_messages results. Read-only on Signal data; writes a decrypted copy to a temporary directory (the temp file is not auto-cleaned). Returns the absolute path to the decrypted file as a string on success, or a string starting with 'Error:' on failure (missing file, wrong key length, or HMAC mismatch). Use this after reading messages that contain attachments.

ParametersJSON Schema
NameRequiredDescriptionDefault
local_keyYesBase64-encoded 64-byte key (32-byte AES-CBC + 32-byte HMAC-SHA256), from the 'local_key' field of attachment metadata.
encrypted_pathYesAbsolute filesystem path to the encrypted attachment file, as returned in the 'encrypted_path' field of message attachment metadata.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Adds substantial context beyond the annotations: it writes a decrypted copy to a temp directory that is NOT auto-cleaned, is read-only on Signal data, and returns either an absolute path or an 'Error:'-prefixed string for the three specific failure modes (missing file, wrong key length, HMAC mismatch). This is exactly the state-changing side effect the annotations (readOnlyHint=false) imply but do not explain.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads what it does and how to use it, then covers side effects and error format. Five sentences with no filler, though the parameter-provenance sentence slightly overlaps the schema descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite an output schema existing, the description still usefully documents the success/error string contract and the non-auto-cleaned temp file. Combined with full parameter coverage and clear invocation context, nothing an agent needs to call this safely is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the parameter formats are documented, but the description adds provenance — both values come from attachment metadata in read_messages or search_messages results — which tells the agent where to source valid inputs. That is marginal value beyond the schema, above the baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Decrypt) and resource (locally stored Signal attachment) plus the return value (path to the decrypted file). No sibling tool does anything similar, so the agent can distinguish it immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this after reading messages that contain attachments' and identifies that encrypted_path and local_key come from read_messages or search_messages results, giving clear context. It does not, however, state any when-not condition or name alternatives for the case where decryption fails.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_statusA
Read-onlyIdempotent

Check whether the Signal MCP server can read local messages and send outbound messages.

Returns boolean fields: source_dir_exists, signal_cli_available, signal_account_configured, read_available, send_available. Read-only with no side effects. Call this before read or send operations to verify configuration; call pairing_status instead to check first-run device-linking state.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so 'Read-only with no side effects' is largely redundant. However, the description adds real context by enumerating the capability checks performed (source dir, CLI availability, account configured, read/send availability), which tells the agent what a successful verification actually confirms.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose, then usage routing, then return fields — a sensible order. It is slightly padded: the return-field list duplicates the output schema and 'Read-only with no side effects' duplicates the annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With zero params, rich annotations, and an output schema documenting the boolean return values, the description covers purpose, usage routing, and safety profile. Nothing an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4. The description adds no parameter detail, but none is needed since the input schema is an empty object with additionalProperties=false.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: checking whether the Signal MCP server can read local messages and send outbound messages. It explicitly names the sibling it is not (pairing_status), so an agent can distinguish it without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use ('Call this before read or send operations to verify configuration') and a when-to-use-the-alternative rule ('call pairing_status instead to check first-run device-linking state'). Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_chatsA
Read-onlyIdempotent

List direct and group Signal chats from the local desktop database, sorted by most recent message.

Each result includes name, phone number, message count, and a preview of the last message. Read-only with no side effects. Use this to discover exact chat names before calling read_messages or search_messages. Use list_groups instead when you need group_id values for send_message.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of chats to return, between 1 and 200. Defaults to 50.
queryNoCase-insensitive substring to filter by chat name or phone number. Empty string returns all chats.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, non-destructive, and closed-world, so 'Read-only with no side effects' is largely redundant. However, the description adds behavior the annotations do not: the sort order (most recent message) and the local-database scope, which help predict results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and sort order are front-loaded, followed by result fields and routing guidance; every sentence contributes. The 'Read-only with no side effects' clause is redundant with the annotations and is the one phrase that does not earn its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With rich annotations, a 100%-covered input schema, and an output schema handling return values, the description covers everything an agent needs: what is returned, ordering, safety, and which sibling to prefer. No material gap remains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so limit and query are already fully documented with defaults and ranges. The description adds no parameter-level detail (e.g., filtering semantics or pagination interaction) beyond what the schema provides, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('List direct and group Signal chats'), names the data source ('local desktop database'), and the ordering ('sorted by most recent message'). It also distinguishes itself from the list_groups sibling, so an agent can select it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names the downstream use case ('discover exact chat names before calling read_messages or search_messages') and the exclusion ('Use list_groups instead when you need group_id values for send_message'). Both when-to-use and the alternative tool are spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_groupsA
Read-onlyIdempotent

List Signal groups with group_id, name, description, members, and admin lists, sorted alphabetically.

Read-only with no side effects. Queries signal-cli when available; falls back to the local desktop database (without group_id). Use this to obtain group_id values needed by send_message. Use list_chats instead for a combined view of both direct and group chats.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of groups to return, between 1 and 200. Defaults to 50.
queryNoCase-insensitive substring to filter group names. Empty string returns all groups.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the description correctly adds value beyond them by disclosing the data source: signal-cli when available, with a fallback to the local desktop database that omits group_id. That degradation caveat is real behavioral context an agent must know. It stops short of detailing result shape, but an output schema exists to carry that.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core verb and resource lead, followed by behavior, then routing. Every sentence carries a distinct load – fields, safety, data source, and two sibling disambiguations – with no padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a filtered, read-only list tool this is complete: it covers purpose, safety, data-source fallback behavior, pagination-adjacent filtering via schema, and routing to the correct sibling. The existence of an output schema means return values need not be explained here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both limit and query are fully documented in the schema already. The description adds no syntax or format detail for either parameter, making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("List Signal groups") and enumerates the returned fields (group_id, name, description, members, admin lists) plus sort order. It explicitly distinguishes itself from the sibling list_chats, so an agent can tell the two apart without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives two explicit routing rules: use this to obtain group_id values needed by send_message, and use list_chats instead for a combined direct+group view. Both the when-to-use and the alternative are named, leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

pairing_statusA

Report the Signal device-linking setup state for first-run pairing, so a client can surface the live link QR.

Returns a setup_state envelope: state 'ready' with the linked account once signal-cli is linked; 'awaiting_qr' with a 'qr_payload' sgnl://linkdevice URI to scan in Signal (Settings → Linked devices → Link new device) while a link is in progress (no payload yet on the first call, until the URI is captured); or 'error' when signal-cli is unavailable. Read-only from the caller's view, but the first call on an unlinked device starts a background signal-cli link process as a side effect. Poll this to drive a pairing UI; call get_status instead to check read/send availability once linked.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses a non-obvious side effect beyond the annotations: the first call on an unlinked device starts a background signal-cli link process, and the first call may return 'awaiting_qr' with no payload yet. This complements the readOnlyHint=false / idempotentHint=false annotations with concrete behavioral detail about the state machine and error condition.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then the return envelope, then the side-effect caveat and the sibling routing. Dense but every sentence carries operational value; the state-by-state enumeration is slightly verbose but justified for a polling contract.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter polling tool, it covers the states ('ready', 'awaiting_qr', 'error'), the payload format, the first-call edge case, and the sibling alternative. Although an output schema exists, the inline state description is consistent and leaves no gap an agent needs to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so the baseline is 4; there is nothing to document and nothing to mislead. The description's envelope details are about returns, not parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Report the Signal device-linking setup state for first-run pairing') and scopes it to a QR-surfacing client use case. It clearly distinguishes itself from the sibling get_status by framing this as the pairing-flow tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says to poll this to drive a pairing UI, and names the alternative ('call get_status instead to check read/send availability once linked') with the condition that selects it. When-to-use and when-to-switch are both stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_messagesA
Read-onlyIdempotent

Read messages from a single Signal chat, returned newest-first.

Each message includes sender, date, body text, reactions, and attachment metadata. Read-only with no side effects. Requires an exact chat name from list_chats. Use search_messages instead to find messages by keyword across chats.

ParametersJSON Schema
NameRequiredDescriptionDefault
afterNoISO 8601 datetime; only return messages sent after this time, e.g. '2025-01-15T00:00:00'.
limitNoMaximum number of messages to return, between 1 and 200. Defaults to 20.
beforeNoISO 8601 datetime; only return messages sent before this time, e.g. '2025-02-01T00:00:00'.
offsetNoNumber of messages to skip from the most recent, for pagination (0-10000). Defaults to 0.
chat_nameYesExact chat name as returned by list_chats (case-sensitive).

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false, so the description's 'Read-only with no side effects' mostly reinforces known safety. It adds useful non-annotation behavior: results are newest-first and each message includes reactions and attachment metadata. Auth needs, rate limits, and pagination behavior are not described.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action and ordering, then covers return contents, safety, prerequisite, and alternative routing in four tight sentences. No sentence is wasted or redundant enough to impede scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the output schema exists, annotations cover safety and idempotency, and schema coverage is complete, the description supplies exactly the missing contextual pieces: scope, ordering, required input provenance, and sibling routing. An agent has enough information to select and invoke the tool correctly without needing return-value details in prose.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents all five parameters, including datetime formats, limit range, and pagination offset. The description adds little beyond restating that chat_name must be exact and come from list_chats, which the schema also states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Read messages from a single Signal chat'), specifies the order ('newest-first'), and names the sibling alternative search_messages. An agent can distinguish this tool from list_chats, search_messages, chat_activity, and send_message without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states the prerequisite ('Requires an exact chat name from list_chats') and gives a clear alternative condition ('Use search_messages instead to find messages by keyword across chats'). This tells the agent when to use this tool versus a sibling that could otherwise seem overlapping.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_messagesA
Read-onlyIdempotent

Search Signal message bodies for a keyword, within one chat or across all chats, returned newest-first.

Each result includes sender, date, chat name, body text, reactions, and attachment metadata. Read-only with no side effects. Use this to find messages by content. Use read_messages instead to browse a specific chat chronologically.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of matching messages to return, between 1 and 200. Defaults to 20.
queryYesCase-insensitive substring to search for within message bodies.
chat_nameNoExact chat name to restrict the search to a single chat. Omit to search across all chats.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so 'read-only with no side effects' largely restates structured data. The description does add ordering (newest-first) and the shape of each result, though the result-field list overlaps with the output schema. Useful but partly redundant.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and scope, then usage routing, then return shape. Efficient overall, though the enumerated result fields are somewhat redundant given the output schema is declared.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With full annotations, 100% schema coverage, and an output schema that covers return values, the description's remaining duty is scope, ordering and sibling routing, all of which are present. Nothing needed to invoke the tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so query, limit and chat_name semantics (substring matching, 1-200 bound, omit-to-search-all) are already documented. The description only restates the one-chat-vs-all-chats scoping that chat_name already encodes, adding no syntax or format detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb (search) plus resource (Signal message bodies), stated scope (one chat or all chats), and result ordering (newest-first). It explicitly distinguishes itself from read_messages, so an agent can route between the two without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States the selection condition ('find messages by content') and names the alternative with its own condition ('read_messages instead to browse a specific chat chronologically'). This is explicit when-to-use and when-not-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

send_messageA

Send a text message via Signal to a direct recipient or group.

This is a write operation that delivers a real message through signal-cli. Sends are not reversible from this server — Signal supports user-initiated message deletion only via the official clients. Exactly one of phone_number, group_id, or chat_name must be supplied; providing zero or more than one raises an error. Rate-limited to 1 message per recipient per second and 10 messages per 60-second window globally. Requires signal-cli and SIGNAL_ACCOUNT to be configured — call get_status first to verify send_available is true. Returns target_type, target identifier, and timestamp on success.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageYesThe text message content to send.
group_idNoTarget group ID as returned by list_groups. Mutually exclusive with phone_number and chat_name.
chat_nameNoExact chat name from list_chats; auto-resolves to the matching phone_number, service ID or group_id. Mutually exclusive with the other two recipient fields.
phone_numberNoRecipient phone number in E.164 format (e.g. '+441234567890'). Mutually exclusive with group_id and chat_name.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=false, idempotentHint=false), it discloses that sends are irreversible from this server, the exact rate limits (1/recipient/second, 10 per 60s global), the external dependency on signal-cli plus SIGNAL_ACCOUNT, and the success return fields. This is exactly the kind of context a write tool needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action, then constraints, prerequisites, and return value in a tight sequence with no filler sentences. Every sentence carries operational information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter write tool, the description covers target selection, irreversibility, rate limits, configuration prerequisites, and result fields. With an output schema present, it need not detail return contents further, so nothing material is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds error semantics not present in the schema: supplying zero or more than one recipient field raises an error, making the exactly-one constraint explicit rather than merely mutual exclusivity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence gives a specific verb (send), resource (text message), transport (Signal), and both supported target modes (direct recipient or group). This is unambiguous against siblings like read_messages, search_messages, and list_chats, which are all read/list operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states a concrete prerequisite (call get_status first to verify send_available is true) and the target-selection rule that exactly one of phone_number/group_id/chat_name must be set. It does not name alternatives for non-send cases or say when not to use this tool, so it stops short of the top tier.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 7 tool updatesv0.2.1
    • Changedchat_activity1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of chats to return, between 1 and 200."New value: +"Maximum number of chats to return, between 1 and 200. Defaults to 50."
    • Changedlist_chats1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of chats to return, between 1 and 200."New value: +"Maximum number of chats to return, between 1 and 200. Defaults to 50."
    • Changedlist_groups1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of groups to return, between 1 and 200."New value: +"Maximum number of groups to return, between 1 and 200. Defaults to 50."
    • Addedpairing_status
    • Changedread_messages2 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of messages to return, between 1 and 200."New value: +"Maximum number of messages to return, between 1 and 200. Defaults to 20."
      • changedInput schema / properties / offset / description
        Previous value: -"Number of messages to skip from the most recent, for pagination (0-10000)."New value: +"Number of messages to skip from the most recent, for pagination (0-10000). Defaults to 0."
    • Changedsearch_messages1 field changed
      • changedInput schema / properties / limit / description
        Previous value: -"Maximum number of matching messages to return, between 1 and 200."New value: +"Maximum number of matching messages to return, between 1 and 200. Defaults to 20."
    • Changedsend_message1 field changed
      • changedInput schema / properties / chat_name / description
        Previous value: -"Exact chat name from list_chats; auto-resolves to the matching phone_number or group_id. Mutually exclusive with the other two recipient fields."New value: +"Exact chat name from list_chats; auto-resolves to the matching phone_number, service ID or group_id. Mutually exclusive with the other two recipient fields."
  2. 16 tool updatesv0.1.3
    • Addedchat_activity
    • Addeddecrypt_attachment
    • Changedlist_chats2 fields changed
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of chats to return, between 1 and 200."
      • addedInput schema / properties / query / description
        Added value: +"Case-insensitive substring to filter by chat name or phone number. Empty string returns all chats."
    • Changedlist_groups2 fields changed
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of groups to return, between 1 and 200."
      • addedInput schema / properties / query / description
        Added value: +"Case-insensitive substring to filter group names. Empty string returns all groups."
    • Changedread_messages5 fields changed
      • addedInput schema / properties / after / description
        Added value: +"ISO 8601 datetime; only return messages sent after this time, e.g. '2025-01-15T00:00:00'."
      • addedInput schema / properties / before / description
        Added value: +"ISO 8601 datetime; only return messages sent before this time, e.g. '2025-02-01T00:00:00'."
      • addedInput schema / properties / chat_name / description
        Added value: +"Exact chat name as returned by list_chats (case-sensitive)."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of messages to return, between 1 and 200."
      • addedInput schema / properties / offset / description
        Added value: +"Number of messages to skip from the most recent, for pagination (0-10000)."
    • Changedsearch_messages3 fields changed
      • addedInput schema / properties / chat_name / description
        Added value: +"Exact chat name to restrict the search to a single chat. Omit to search across all chats."
      • addedInput schema / properties / limit / description
        Added value: +"Maximum number of matching messages to return, between 1 and 200."
      • addedInput schema / properties / query / description
        Added value: +"Case-insensitive substring to search for within message bodies."
    • Changedsend_message4 fields changed
      • addedInput schema / properties / chat_name / description
        Added value: +"Exact chat name from list_chats; auto-resolves to the matching phone_number or group_id. Mutually exclusive with the other two recipient fields."
      • addedInput schema / properties / group_id / description
        Added value: +"Target group ID as returned by list_groups. Mutually exclusive with phone_number and chat_name."
      • addedInput schema / properties / message / description
        Added value: +"The text message content to send."
      • addedInput schema / properties / phone_number / description
        Added value: +"Recipient phone number in E.164 format (e.g. '+441234567890'). Mutually exclusive with group_id and chat_name."
    • Removedsignal_chat_activity
    • Removedsignal_get_chat_messages
    • Removedsignal_get_status
    • Removedsignal_list_chats
    • Removedsignal_list_groups
    • Removedsignal_read_messages
    • Removedsignal_search_chat
    • Removedsignal_search_messages
    • Removedsignal_send_message
  3. 15 tool updatesv0.1.2
    • First observedget_status
    • First observedlist_chats
    • First observedlist_groups
    • First observedread_messages
    • First observedsearch_messages
    • First observedsend_message
    • First observedsignal_chat_activity
    • First observedsignal_get_chat_messages
    • First observedsignal_get_status
    • First observedsignal_list_chats
    • First observedsignal_list_groups
    • First observedsignal_read_messages
    • First observedsignal_search_chat
    • First observedsignal_search_messages
    • First observedsignal_send_message

TDQS

A4.4/5.0

Scored across 9 tools

Disambiguation4/5

Most tools target clearly distinct operations, especially read/search/send/decrypt. The main overlap is among list_chats, list_groups, and chat_activity, but descriptions explicitly differentiate their purposes (combined directory, group_id lookup, activity ranking).

Naming Consistency4/5

The tool set uses consistent snake_case and mostly follows a verb_noun or noun_status pattern. pairing_status and chat_activity deviate slightly from the dominant verb_noun convention, but remain readable and predictable.

Tool Count5/5

Nine tools is well-scoped for a Signal integration covering setup, discovery, reading, searching, attachment decryption, and sending. Each tool appears to earn its place without obvious redundancy.

Completeness4/5

The surface covers the core Signal lifecycle: status, pairing, chat/group discovery, message reading/searching, attachment decryption, and sending. Minor gaps exist around sending attachments, reactions, or deletion, but core workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    MCP server for Signal via signal-cli that enables sending and receiving messages, managing contacts and groups, and reacting over stdio.
    2 npm
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for a local-first messaging workspace that integrates Google Messages, WhatsApp, and Signal. It enables reading, sending, searching messages, and managing conversations through MCP tools.
    58
    -