Skip to main content
Glama
NathanDai5287

signal-mcp

signal-mcp

A read-only Model Context Protocol server for Signal Desktop messages. It supports local stdio and authenticated remote Streamable HTTP.

It gives an MCP-capable agent four narrowly scoped tools:

  • signal_list_conversations — find recent direct and group conversations without returning message bodies.

  • signal_get_messages — retrieve a bounded chronological window from one exact conversation.

  • signal_search_messages — literal substring search, optionally scoped by conversation and time.

  • signal_get_message — retrieve one exact message with quote and attachment metadata.

The server never sends messages or executes write statements. Remote mode is opt-in, binds to localhost by default, and requires authentication. See Read-only boundary for an important native-driver limitation.

Important privacy warning

This server can expose your private Signal history to whichever MCP host and model you connect it to. Review that host's data-handling policy, keep requests narrowly scoped, and do not configure this server for agents you do not trust.

Signal Desktop's local database is an implementation detail, not a public API. Signal schema updates may temporarily break this project. This project is unofficial and is not affiliated with or endorsed by Signal Messenger LLC.

Related MCP server: msteams-local-mcp

Current support

  • Windows and Linux

  • Signal Desktop installed for the same operating-system user

  • Node.js 20 or newer

  • Local stdio or remote Streamable HTTP MCP hosts

On Windows, automatic key recovery uses the current user's DPAPI credentials. On Linux, Signal's direct config.json key is read and validated without copying it into this project's configuration.

Install

git clone https://github.com/NathanDai5287/signal-mcp.git
cd signal-mcp
npm install
npm run build

By default the server discovers Signal under %APPDATA%\Signal. No configuration is normally needed.

To test the connection without configuring an MCP host:

npx @modelcontextprotocol/inspector node dist/src/index.js

The Inspector can call tools that return real private messages. Treat its browser session accordingly.

MCP host configuration

Build the project, then configure your host to launch the compiled entry point with an absolute path:

{
  "mcpServers": {
    "signal": {
      "command": "node",
      "args": ["C:\\path\\to\\signal-mcp\\dist\\src\\index.js"]
    }
  }
}

Restart the host after changing its MCP configuration. The exact configuration file location depends on the host.

Remote HTTP deployment

Set SIGNAL_MCP_TRANSPORT=http, an HTTPS SIGNAL_MCP_PUBLIC_URL ending in /mcp, and one authentication mode. The listener defaults to 127.0.0.1:8788 and is intended to sit behind a TLS tunnel or reverse proxy.

  • static validates a fixed Bearer token. It works with Claude Code and API integrations that can supply an Authorization header.

  • oidc validates issuer-signed JWT access tokens and publishes MCP protected-resource metadata. Use it for normal ChatGPT and hosted Claude account linking. The issuer must expose OAuth or OpenID Connect discovery metadata, a JWKS URI, authorization-code flow with S256 PKCE, and a client-registration mechanism accepted by the target host.

The Minmus instance runs as a hardened systemd service with its environment in the root-only /etc/signal-mcp.env file. It remains bound to localhost behind the Cloudflare Tunnel; the one-time deployment script is intentionally not retained.

Optional configuration

Environment variables are passed through the MCP host's server configuration:

Variable

Purpose

Default

SIGNAL_MCP_DB

Signal SQLCipher database path

%APPDATA%\Signal\sql\db.sqlite

SIGNAL_MCP_CONFIG

Signal config.json path

%APPDATA%\Signal\config.json

SIGNAL_MCP_LOCAL_STATE

Signal Chromium Local State path

%APPDATA%\Signal\Local State

SIGNAL_MCP_KEY

Explicit 64-character SQLCipher key

Recovered with DPAPI

SIGNAL_MCP_MAX_MESSAGES

Per-call message cap (cannot exceed 200)

200

SIGNAL_MCP_MAX_CONVERSATIONS

Conversation cap (cannot exceed 100)

100

SIGNAL_MCP_TRANSPORT

stdio or authenticated http

stdio

SIGNAL_MCP_HOST / SIGNAL_MCP_PORT

Remote listener

127.0.0.1:8788

SIGNAL_MCP_PUBLIC_URL

Canonical public HTTPS URL ending in /mcp

Required for HTTP

SIGNAL_MCP_AUTH_MODE

static or oidc

static

SIGNAL_MCP_BEARER_TOKEN

Fixed token, at least 32 characters

Required for static mode

SIGNAL_MCP_OAUTH_ISSUER

OAuth/OIDC issuer URL

Required for OIDC mode

SIGNAL_MCP_OAUTH_AUDIENCE

Expected JWT audience

Required for OIDC mode

SIGNAL_MCP_REQUIRED_SCOPES

Comma-separated access-token scopes

signal:read

SIGNAL_MCP_ALLOWED_ORIGINS

Additional permitted browser origins

None

SIGNAL_MCP_MAX_BODY_BYTES

HTTP request cap, up to 4 MiB

1048576

SIGNAL_MCP_RATE_LIMIT_PER_MINUTE

Per-client request limit

120

Avoid putting SIGNAL_MCP_KEY in a committed file. Automatic recovery is preferable on Windows.

How database access works

On Windows, Signal Desktop stores an AES-256-GCM-encrypted SQLCipher key in config.json; its wrapping key is in Chromium's Local State, protected with DPAPI. Linux Signal stores a direct key in the same config directory. At startup this server:

  1. Reads and validates the Linux direct key, or DPAPI-unprotects and decrypts the Windows key in memory.

  2. Opens the database with SQLCipher compatibility level 4.

  3. Disables automatic WAL checkpoints and enables SQLite query_only mode before serving any tool calls.

The encrypted DPAPI payload is sent to the system PowerShell process over stdin, never on its command line. Subprocess output buffers and cryptographic buffers are cleared after use. Raw subprocess, filesystem, and startup errors are not printed or returned through MCP. The final SQLCipher key must briefly exist in managed memory so it can be passed to the addon's PRAGMA API; it is never intentionally logged, returned through MCP, or persisted by this project.

All search inputs are bound SQL parameters. %, _, and \ are escaped so search terms are literal substrings rather than caller-controlled SQL patterns. Every result set and serialized tool response is capped. Repeated calls can still page through all available history; connecting a host grants it that capability.

Read-only boundary

The official @signalapp/sqlcipher package currently opens files with SQLite's read-write/create flags and does not expose a read-only open option. This server immediately enables and verifies PRAGMA query_only = ON, contains no write queries, and disables automatic WAL checkpoints. Those controls prevent SQL writes through this connection, but they are not the same as an OS/file-level read-only handle: SQLite may still open or manage -wal/-shm sidecar state. A truly file-level read-only open requires an upstream change or maintained fork of the native addon.

Data returned

Message results can contain:

  • Signal message and conversation identifiers

  • millisecond timestamps

  • incoming/outgoing direction

  • sender service ID and display name (phone numbers are not used as name fallbacks)

  • message body

  • denormalized quoted-reply text

  • attachment metadata and local-download availability

Tool responses use compact JSON. Fields whose value would be null, empty, or a default are omitted when their absence is unambiguous. Message rowId values are internal and never returned. Conversation-scoped tools omit each message's redundant conversationId; global search results retain it so matches can be attributed to a conversation. Conversation types are normalized to direct or group.

This milestone does not decrypt or return attachment bytes, create a durable archive, perform semantic search, or send messages. Disappearing messages that Signal has already deleted cannot be recovered.

View-once attachment details are intentionally reduced to { "isViewOnce": true }. Filenames, media types, sizes, and download state are not returned for them.

Pagination and ordering

Conversation lists and message tools return hasMore. For conversations, increase limit up to the configured maximum when hasMore is true; conversations beyond that cap are not returned. Message tools return chronological rows and, when applicable, sizeTruncated: true plus an opaque nextCursor; absent values mean the result was not size-truncated and there is no next cursor. Pass nextCursor back as cursor with the same direction:

  • direction: "older" starts at the newest matching page and walks backward.

  • direction: "newer" starts at the oldest row after the supplied time bound and walks forward.

before and after are exclusive millisecond bounds on Signal's receive-order timestamp (falling back to the sent timestamp for legacy nulls). Composite receive-time/row-ID cursors prevent messages with tied timestamps from being skipped. Results are rendered chronologically in either direction.

Global substring search scans Signal's message table and may be slower than conversation-scoped search on large histories.

Development

npm test
npm run check
npm run build

The test suite uses fake MCP readers and temporary encrypted SQLCipher fixture databases. It does not access your Signal data. A manual local smoke test is still required to detect changes in Signal's private database schema.

Operational logging goes to stderr because stdout is reserved for MCP JSON-RPC traffic.

Security

Please do not open public issues containing message text, database files, keys, service IDs, phone numbers, or local paths. See SECURITY.md for private vulnerability reporting guidance.

License

The project source is MIT licensed. Dependencies retain their own licenses; notably, @signalapp/sqlcipher is distributed under AGPL-3.0-only.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables querying Signal Desktop chats and messages by reading the encrypted SQLite database directly, providing tools for listing chats, searching messages, and running read-only SQL queries.
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    A read-only MCP server that lets AI search a user's own LINE Desktop chat history on macOS, providing tools to list chats, retrieve messages, and search conversations directly from the local encrypted database.
    5
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to read the entire Apple Messages (iMessage/SMS) history on a Mac through a read-only, batched tool that supports listing chats, retrieving transcripts, polling recent messages, and searching message bodies via REST or streamable HTTP MCP.
    MIT