Skip to main content
Glama
express-reader-mcp

express-reader-mcp

by Eflarus

express-reader-mcp

Read-only MCP server that reads your own eXpress desktop chats from the running app. Built for a public-server account, for which the CTS-only community MCPs do not work.

⚠️ Unofficial. This is an independent, community project. It is not affiliated with, endorsed by, or supported by eXpress, its developer, or any official eXpress development team. It relies on undocumented internals of the desktop app, may break at any time, and is used entirely at your own risk. Only for reading your own account.

How it works

The eXpress desktop app is Electron. On demand, the server sends SIGUSR1 to the running app, which opens Node's inspector on 127.0.0.1:9229 (loopback only). Through it the server reads the app's in-memory Redux store, which holds your chats already decrypted by the app itself. No new device is registered; no account keys leave the app.

Related MCP server: eXpress MCP connector

Install

TypeScript (strict), compiled to dist/. Requires Node ≥ 22.

npm install
npm run build

Register with your MCP client, pointing at the built entry point, e.g. for Claude Code:

claude mcp add --scope user express-reader -- "$(which node)" "$PWD/dist/server.js"

Tools

  • express_status — signed-in user, chat/unread counts, which chat is open. Silent.

  • express_list_chats — chat list with metadata; filter by query/unreadOnly. Silent.

  • express_read_chat — resolve a person by name (or exact chatId), open the chat, return messages over sinceDays. Pages back through the app's history scroll to cover ranges well beyond the initial ~1 week. Each message carries syncId, attachment metadata (kind/fileName/size/mime/fileId/hasImagePreview), and hasThread/isReply flags. Marks the chat read and may send the sender a read receipt. coversWholeRange tells you if the full range was reached; historyExhausted means there are no older messages.

  • express_search_messages — native server-side search within a chat (resolve by name or chatId): finds matches across the whole history, fast, not just loaded messages. Returns matching messages (syncId/time/from/text + attachment metadata); optional sinceDays. Opens the chat (marks read).

  • express_open_link — open an xlnk.ms/open/message?sync_id=&chat_id= deep link and return the referenced message + surrounding context. Opens (marks read) the linked chat.

  • express_list_threads — list a chat's threads (threadId + reply count). Side-effect-free.

  • express_read_thread — read a thread's replies by chatId+threadId. Opens the parent chat (marks read).

  • express_get_image — save an image message's decrypted preview thumbnail to a local file (by chatId+syncId) for quick viewing. Thumbnail only; use express_download_file for the full-resolution image.

  • express_download_file — download a document/image attachment at full fidelity. The app decrypts it to an in-memory blob; the file is saved to a local folder (default ~/Downloads/express-reader, configurable via folder or EXPRESS_DOWNLOAD_DIR) and the path is returned so you can read it as a normal file. Keys never leave the app. Max 25 MiB. Opens the chat (marks read).

  • express_close_debug — quit + relaunch eXpress to unbind the debug port when finished.

Security model

  • While a read is in progress the inspector port is open on loopback; any local process could attach to the app during that window. Call express_close_debug when done.

  • Read-only: there is no send capability.

  • Requires the app to be running and signed in. Node ≥ 22 (global WebSocket/fetch).

How downloads stay safe

express_download_file never touches your encryption keys. It calls the app's own loadAttachment, which decrypts the file in-app to an in-memory blob; the tool only fetches that already-decrypted blob (the same way it reads a decrypted message) and writes it to disk. The key material stays inside the app at all times.

Limits / notes

  • Reading a non-open chat still marks it read (the reliable path opens the chat); fully silent reading is not wired.

  • History paging walks the app's virtualized scroll; very long ranges take a few seconds per ~50-message page (maxScrolls caps it).

  • Downloads cap at 25 MiB.

  • Tied to the app's internal store/route shapes (observed on app v3.73.51); an app update may require adjustment.

Available Tools

10 tools
express_close_debugA

Close the debug inspector port by gracefully quitting and relaunching eXpress. Call when finished reading, to stop exposing 127.0.0.1:9229.

Inputs: no named parameters.

express_download_fileA

Download a document/file attachment: the app decrypts it and the file is saved to a local folder, then you can read it as a normal file (returns the path). Needs chatId + the message syncId (from read/open/thread output). Saves into ~/Downloads/express-reader by default; override with folder (relative = subfolder of ~/Downloads, absolute = used as-is) or the EXPRESS_DOWNLOAD_DIR env var. Max 25 MiB. SIDE EFFECT: opens the chat (marks read).

Inputs: chatId (required), syncId (required), folder.

express_get_imageA

Fetch an image message's decrypted preview thumbnail and save it to a local file (returns the path, which you can then read/view). Needs the chatId and the message syncId (from express_read_chat / express_open_link output). Only images have previews; this is a thumbnail, not full resolution. SIDE EFFECT: opens the chat if not already active (marks read).

Inputs: chatId (required), syncId (required).

express_list_chatsA

List chats with metadata (name, type, unread count, last-activity time). Side-effect-free. Use query to filter by name/transliteration, unreadOnly to show only unread.

Inputs: limit, query, unreadOnly.

express_list_threadsA

List the threads belonging to a chat (threadId + reply count). Side-effect-free. Use the returned threadId with express_read_thread.

Inputs: chatId (required).

express_read_chatA

Open a chat and return its decrypted messages over the last sinceDays days. Pages back through the app's history scroll to cover ranges beyond the initial ~1 week. Resolve the person by name OR pass an exact chatId; if name matches several chats, the matches are returned instead of reading. WARNING: opening a chat MARKS IT READ and may send the sender a read receipt. coversWholeRange: false means the range wasn't fully reached within maxScrolls; historyExhausted means there are no older messages.

Inputs: name, limit, chatId, sinceDays, +2 more.

express_read_threadA

Read a thread's replies by chatId + threadId (from express_list_threads). Messages include attachment metadata. WARNING: opens the parent chat (MARKS IT READ) and navigates into the thread.

Inputs: chatId (required), threadId (required), limit.

express_search_messagesA

Native server-side search within a chat — finds matches across the WHOLE history (not just loaded messages), fast. Resolve the chat by name or exact chatId; if name matches several, the matches are returned instead of searching. Returns matching messages (syncId/time/from/text + attachment metadata). Optionally filter with sinceDays. truncated: true means the app capped results (~100). WARNING: opens the chat (MARKS IT READ).

Inputs: query (required), name, limit, chatId, +1 more.

express_statusA

Check the running eXpress app: signed-in user, chat count, unread count, which chat is open. Side-effect-free (no read receipts).

Inputs: no named parameters.

Tool Schema Changelog

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

    TDQS

    A4/5.0

    Scored across 10 tools

    Disambiguation4/5

    Most tools have clearly distinct purposes: status, listing chats, reading chats, searching, opening links, threads, images, and file downloads. The main overlap is between express_read_chat, express_search_messages, and express_open_link, since all can return message content and all may mark a chat read, but the descriptions explain when to use each.

    Naming Consistency4/5

    Nearly all tools follow a predictable `express_verb_noun` pattern, such as express_list_chats, express_read_chat, and express_download_file. The only notable deviation is express_status, which uses a noun rather than a verb, but the overall convention remains clear.

    Tool Count5/5

    Ten tools is well scoped for an app-reading MCP server. Each tool covers a distinct operation, from status and chat listing to thread reading, image retrieval, and file download, with no obvious redundancy.

    Completeness4/5

    The surface covers the core read lifecycle: status, chat listing, message reading, search, link resolution, threads, images, and file attachments. The main gap is that there is no side-effect-free way to read message content, since every message-reading tool may mark the chat as read; additional gaps include no thread-level search and limited non-scrolling pagination.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Provides read-only access to Signal Desktop message history, enabling listing conversations, retrieving messages, and searching messages through MCP tools.
      1
      MIT
    • F
      license
      A
      quality
      C
      maintenance
      Enables reading and searching a user's eXpress chats and message history locally, with optional confirmed sending of text messages.
      4
      -
    • A
      license
      A
      quality
      C
      maintenance
      Enables an agent to read the user's local Messages.app database on macOS, listing chats and threads and searching iMessage/SMS history entirely on localhost, with optional AppleScript sending gated behind an explicit flag. Access is read-only and scoped-shut by default, and replies can be optionally sent when send mode is enabled.
      4
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables agents to perform read-only access to explicitly permitted chats on Telegram Web, including history, native search, post comments, photos and files, multi-chat context collection, and persistent local conversation history via a dedicated browser profile without Telegram API registration.
      10
      MIT