signal-mcp
A Node/TypeScript MCP server that readsSignal Desktop's encrypted SQLite database directly and exposes richer querytools than the original Python signal-mcp-server.
Read-only. The database is opened with
readonly: trueandquery_only=ON. The server cannot modify Signal's data.
Requirements
- macOS, with Signal Desktop installed and at least signed in once.
- Node.js 20+.
- On first run you'll see one macOS Keychain prompt — approve it (and checkAlways Allow if you don't want to be asked again). The server reads theSignal
safeStoragepassword from your login keychain to decrypt theSQLCipher key.
Install
Step 1 — clone and install deps
git clone https://github.com/jagypus/signal-mcp.git
cd signal-mcp
npm install
The repo ships a pre-built dist/ so no compile step is needed; npm installjust pulls runtime deps.
Step 2 — register with your Claude client
The two clients share the same MCP server JSON shape but read it fromdifferent files. Pick whichever you use.
Claude Code
One-liner via the CLI:
claude mcp add signal --scope user -- node "$(pwd)/dist/index.js"
Or edit ~/.claude.json (or a project .mcp.json) and add:
{
"mcpServers": {
"signal": {
"command": "node",
"args": ["/Users/you/signal-mcp/dist/index.js"]
}
}
}
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)or %APPDATA%\Claude\claude_desktop_config.json (Windows) and add:
{
"mcpServers": {
"signal": {
"command": "node",
"args": ["/Users/you/signal-mcp/dist/index.js"]
}
}
}
Then fully quit and relaunch Claude Desktop — it only reads this file atstartup. (Claude Code picks up claude mcp add immediately, but you'll needto restart any active sessions.)
Step 3 — verify
- Claude Code:
claude mcp listshould includesignal. - Claude Desktop: check the connectors / tools panel; the five
signaltools should be listed.
Then ask: "List my Signal chats."
Updating
cd /path/to/signal-mcp
git pull
npm install
Restart Claude Desktop, or restart your Claude Code session, to pick upchanges.
Removing
# Claude Code:
claude mcp remove signal
# Claude Desktop: delete the "signal" entry from claude_desktop_config.json,
# then relaunch the app.
rm -rf /path/to/signal-mcp
Why not npm install -g git+https://...?
It looks attractive but currently fails on npm 11.x: the global-install pathmishandles native-dep install scripts (better-sqlite3-multiple-ciphers) andpartially-extracts the package tarball, leaving a broken install. Theclone-and-register flow above sidesteps the issue entirely.
Tools
| Tool | Purpose |
|---|---|
list_chats |
List conversations with last-message metadata, filterable by group/DM, message count, and recency. |
get_recent_messages |
Cross-chat message query with date range, sender, and chat filters. |
get_chat_messages |
Same filter set, scoped to a single chat (by id or name). |
search_messages |
Full-text-ish search across all message bodies. Falls back to LIKE. |
query_sql |
Read-only SQL passthrough (SELECT/WITH/EXPLAIN/PRAGMA). |
All inputs are validated with Zod. Timestamps are ISO 8601 in/out.
Filtering rules
exclude_system(defaulttrue) keeps onlytype IN ('incoming','outgoing'),filtering outkeychange,profile-change,group-v2-change,timer-notification, etc.only_with_body(defaulttrue) excludes attachment-only / reaction /sticker rows wherebody IS NULL.sender:me(outgoing),them(incoming),any(both).
Development
git clone https://github.com/jagypus/signal-mcp.git
cd signal-mcp
npm install
npm run build # compile to dist/
npm run dev # tsx, stdio (no build step)
npm run probe # dump schema/FTS/types against the live DB
npx tsx scripts/smoke.ts # exercise every tool against the live DB
How it opens the DB
Signal Desktop on macOS stores the SQLCipher v4 database at~/Library/Application Support/Signal/sql/db.sqlite. Modern Signal versionsstore the SQLCipher key encrypted in config.json under encryptedKey usingElectron's safeStorage:
- Strip
v10/v11prefix → AES-128-CBC ciphertext. - Encryption key = PBKDF2-HMAC-SHA1(password, "saltysalt", 1003 iters, 16 bytes).
- On macOS,
passwordis fetched viasecurity find-generic-password -s "Signal Safe Storage" -a "Signal" -w(one keychain prompt the first time). - IV is 16 bytes of
0x20.
The plaintext is the 64-char hex SQLCipher key. Older Signal builds with aplaintext key in config.json are also supported.
The DB is opened with better-sqlite3-multiple-ciphers in readonly: truemode and query_only=ON is set as a belt-and-braces guard. Opening whileSignal Desktop is running works fine because SQLCipher uses WAL.
Search caveat
messages_fts exists but uses Signal's custom signal_tokenizer which isregistered only by Signal Desktop's native code. Third-party readers can't runMATCH queries against it, so search_messages probes once and silentlyfalls back to body LIKE '%query%'.
Environment
| Var | Effect |
|---|---|
SIGNAL_DIR |
Override default Signal data directory (useful for fixtures). |
SIGNAL_KEY |
64-char hex SQLCipher key, bypassing config.json/Keychain. |
Cross-platform notes
- macOS: handled.
- Linux:
safeStoragev10 uses the literal passwordpeanuts. v11 (libsecret/KWallet) is not implemented — setSIGNAL_KEYexplicitly. - Windows: not implemented — set
SIGNAL_KEYexplicitly.
Project layout
src/
index.ts # MCP server bootstrap
db.ts # connection + safeStorage key decryption
schema.ts # zod input shapes
util/
time.ts # iso <-> ms
messages.ts # row shaping, display name resolution
sql.ts # shared filter SQL
tools/
listChats.ts
getRecentMessages.ts
getChatMessages.ts
searchMessages.ts
querySql.ts
scripts/
probe.ts # live-DB schema dump
smoke.ts # live-DB end-to-end check
License
MIT — see LICENSE.
This project is not affiliated with or endorsed by Signal Messenger LLC.Signal Desktop itself is licensed under AGPL-3.0; this project does notredistribute or modify any Signal code, it only reads the local SQLitedatabase that Signal Desktop creates on your own machine.