express-reader-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@express-reader-mcplist my unread chats from the last 24 hours"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 buildRegister 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 byquery/unreadOnly. Silent.express_read_chat— resolve a person byname(or exactchatId), open the chat, return messages oversinceDays. Pages back through the app's history scroll to cover ranges well beyond the initial ~1 week. Each message carriessyncId,attachmentmetadata (kind/fileName/size/mime/fileId/hasImagePreview), andhasThread/isReplyflags. Marks the chat read and may send the sender a read receipt.coversWholeRangetells you if the full range was reached;historyExhaustedmeans there are no older messages.express_search_messages— native server-side search within a chat (resolve bynameorchatId): finds matches across the whole history, fast, not just loaded messages. Returns matching messages (syncId/time/from/text + attachment metadata); optionalsinceDays. Opens the chat (marks read).express_open_link— open anxlnk.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 bychatId+threadId. Opens the parent chat (marks read).express_get_image— save an image message's decrypted preview thumbnail to a local file (bychatId+syncId) for quick viewing. Thumbnail only; useexpress_download_filefor 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 viafolderorEXPRESS_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_debugwhen 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 (
maxScrollscaps 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 toolsexpress_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_open_linkA
Open an eXpress deep link (e.g. https://xlnk.ms/open/message?sync_id=...&chat_id=...) and return the referenced message plus surrounding context. Each message includes attachment metadata and flags (hasThread, isReply). WARNING: opens the linked chat, which MARKS IT READ.
Inputs: url (required), context.
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
Scored across 10 tools
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.
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.
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.
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
Related MCP Connectors
Read-only access to your MetaTrader 4 and 5 accounts for AI agents: balances, open positions, trade history with win rate, live quotes, symbol specs and candles. It cannot place, modify or close orders.
Read-only MCP access to authorized Vocci sessions, notes, files, and memory search.
Read Spruce Health contacts, conversations, messages, phone lines and team members (read-only).
Read-only OXVO Insights analytics: list tracked events and run queries for one app you authorize.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceProvides read-only access to Signal Desktop message history, enabling listing conversations, retrieving messages, and searching messages through MCP tools.1MIT
- FlicenseAqualityCmaintenanceEnables reading and searching a user's eXpress chats and message history locally, with optional confirmed sending of text messages.4-
- AlicenseAqualityCmaintenanceEnables 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.4MIT
- AlicenseAqualityCmaintenanceEnables 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.10MIT